Passed
Push — task/hygrogen-react-components ( 0716f4...cc1c06 )
by Yonathan
04:18
created

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

Complexity

Total Complexity 1
Complexity/F 0

Size

Lines of Code 221
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

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