Passed
Push — task/update-profile-experience ( 3aacad...e72667 )
by Tristan
05:18
created

resources/assets/js/components/H2Components/Dialog.tsx   A

Complexity

Total Complexity 1
Complexity/F 0

Size

Lines of Code 183
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 1
eloc 141
c 0
b 0
f 0
dl 0
loc 183
rs 10
mnd 1
bc 1
fnc 0
bpm 0
cpm 0
noi 0
1
import * as React from "react";
2
import {
3
  h2ComponentDialogLoad,
4
  h2ComponentDialogLoadResize,
5
  h2ComponentDialogEnableTrigger,
6
} from "@hydrogen-design-system/system/dist/import/latest/components/dialog/scripts/dialog";
7
import { GeneralProps, GeneralBtnProps } from "./utils";
8
9
interface DialogContext extends GeneralProps {
10
  /** The dialogs id. */
11
  id: string;
12
}
13
14
const DialogContext = React.createContext<DialogContext | undefined>(undefined);
15
16
/**
17
 * This Context hook allows our child components to easily reach
18
 * into the Tabs context and get the pieces it needs.
19
 *
20
 * Bonus: it even makes sure the component is used within a
21
 * Dialog component!
22
 */
23
const useDialogContext = (): Partial<DialogContext> => {
24
  const context = React.useContext(DialogContext);
25
  if (!context) {
26
    throw new Error("This component must be used within a <Dialog> component.");
27
  }
28
  return context;
29
};
30
31
const Actions: React.FunctionComponent<GeneralProps> = (props) => {
32
  useDialogContext(); // Ensures sub-component can only be used within the Dialog component.
33
  const { className, children, ...rest } = props;
34
  return (
35
    <div data-h2-dialog-actions className={className} {...rest}>
36
      {children}
37
    </div>
38
  );
39
};
40
41
const ActionBtn: React.FunctionComponent<GeneralBtnProps & GeneralProps> = (
42
  props,
43
) => {
44
  const { id } = useDialogContext();
45
  const { buttonStyling, type, onClick, className, children, ...rest } = props;
46
  const ref = React.useRef(null);
47
  React.useEffect((): void => {
48
    h2ComponentDialogEnableTrigger("latest", ref.current);
49
  });
50
  return (
51
    <button
52
      data-h2-dialog-trigger={`${id}`}
53
      data-h2-button={buttonStyling}
54
      ref={ref}
55
      type={type || "button"}
56
      onClick={onClick}
57
      className={className}
58
      {...rest}
59
    >
60
      {children}
61
    </button>
62
  );
63
};
64
65
const Content: React.FunctionComponent<GeneralProps> = (props) => {
66
  const { id } = useDialogContext();
67
  const { className, children, ...rest } = props;
68
  return (
69
    <div
70
      data-h2-dialog-content
71
      id={`${id}Content`}
72
      className={className}
73
      {...rest}
74
    >
75
      {children}
76
    </div>
77
  );
78
};
79
80
const Header: React.FunctionComponent<GeneralProps> = (props) => {
81
  useDialogContext(); // Ensures sub-component can only be used within the Dialog component.
82
  const { className, children, ...rest } = props;
83
  return (
84
    <div data-h2-dialog-title className={className} {...rest}>
85
      {children}
86
    </div>
87
  );
88
};
89
90
interface OverlayProps {
91
  overlay?: string;
92
}
93
94
const Overlay: React.FunctionComponent<OverlayProps> = (props) => {
95
  const { overlay } = props;
96
  return <div data-h2-dialog-overlay={`${overlay || "black, .9"}`} />;
97
};
98
99
const Title: React.FunctionComponent<GeneralProps> = (props) => {
100
  const { id } = useDialogContext();
101
  const { className, children, ...rest } = props;
102
  return (
103
    <h5 data-h2-focus id={`${id}Title`} className={className} {...rest}>
104
      {children}
105
    </h5>
106
  );
107
};
108
109
interface TriggerProps extends GeneralProps, GeneralBtnProps {
110
  id: string;
111
}
112
113
/** This Trigger component opens the dialog and sits outside the main dialog component */
114
const Trigger: React.FunctionComponent<TriggerProps> = (props) => {
115
  const { id, buttonStyling, className, children, ...rest } = props;
116
  const ref = React.useRef(null);
117
  React.useEffect((): void => {
118
    h2ComponentDialogEnableTrigger("latest", ref.current);
119
  });
120
  return (
121
    <button
122
      data-h2-button={buttonStyling}
123
      ref={ref}
124
      data-h2-dialog-trigger={`${id}`}
125
      type="button"
126
      className={className}
127
      {...rest}
128
    >
129
      {children}
130
    </button>
131
  );
132
};
133
134
interface DialogComposition {
135
  Actions: React.FunctionComponent<GeneralProps>;
136
  ActionBtn: React.FunctionComponent<GeneralBtnProps>;
137
  Content: React.FunctionComponent<GeneralProps>;
138
  Header: React.FunctionComponent<GeneralProps>;
139
  Overlay: React.FunctionComponent<OverlayProps>;
140
  Title: React.FunctionComponent<GeneralProps>;
141
  Trigger: React.FunctionComponent<TriggerProps>;
142
}
143
144
const Dialog: React.FunctionComponent<DialogContext> & DialogComposition = (
145
  props,
146
) => {
147
  const { id, className, children, ...rest } = props;
148
  React.useEffect(() => {
149
    h2ComponentDialogLoad();
150
    h2ComponentDialogLoadResize();
151
    h2ComponentDialogEnableTrigger();
152
  });
153
  return (
154
    <DialogContext.Provider value={props}>
155
      <div
156
        aria-hidden="true"
157
        aria-describedby={`${id}Content`}
158
        aria-labelledby={`${id}Title`}
159
        data-h2-dialog={id}
160
        tabIndex={-1}
161
        role="dialog"
162
      >
163
        <div data-h2-dialog-wrapper className={className} {...rest}>
164
          {children}
165
        </div>
166
      </div>
167
    </DialogContext.Provider>
168
  );
169
};
170
171
// We expose the children components here, as properties.
172
// Using the dot notation we explicitly set the composition relationships,
173
// btw the Dialog component and its sub components.
174
Dialog.Actions = Actions;
175
Dialog.ActionBtn = ActionBtn;
176
Dialog.Content = Content;
177
Dialog.Header = Header;
178
Dialog.Overlay = Overlay;
179
Dialog.Title = Title;
180
Dialog.Trigger = Trigger;
181
182
export default Dialog;
183