Passed
Push — feature/azure-webapp-pipeline-... ( 7fdaeb...271549 )
by Grant
05:11
created

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

Complexity

Total Complexity 1
Complexity/F 0

Size

Lines of Code 174
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 1
eloc 135
mnd 1
bc 1
fnc 0
dl 0
loc 174
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
import * as React from "react";
2
import {
3
  h2DialogResizeOnViewport,
4
  h2DialogTrigger,
5
  h2DialogEnableOpenDialogs,
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
    h2DialogTrigger(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
const Title: React.FunctionComponent<GeneralProps> = (props) => {
91
  const { id } = useDialogContext();
92
  const { className, children, ...rest } = props;
93
  return (
94
    <h5 data-h2-focus id={`${id}Title`} className={className} {...rest}>
95
      {children}
96
    </h5>
97
  );
98
};
99
100
interface TriggerProps extends GeneralProps, GeneralBtnProps {
101
  id: string;
102
}
103
104
/** This Trigger component opens the dialog and sits outside the main dialog component */
105
const Trigger: React.FunctionComponent<TriggerProps> = (props) => {
106
  const { id, buttonStyling, className, children, ...rest } = props;
107
  const ref = React.useRef(null);
108
  React.useEffect((): void => {
109
    h2DialogTrigger(ref.current);
110
  });
111
  return (
112
    <button
113
      data-h2-button={buttonStyling}
114
      ref={ref}
115
      data-h2-dialog-trigger={`${id}`}
116
      type="button"
117
      className={className}
118
      {...rest}
119
    >
120
      {children}
121
    </button>
122
  );
123
};
124
125
interface DialogComposition {
126
  Actions: React.FunctionComponent<GeneralProps>;
127
  ActionBtn: React.FunctionComponent<GeneralBtnProps>;
128
  Content: React.FunctionComponent<GeneralProps>;
129
  Header: React.FunctionComponent<GeneralProps>;
130
  Title: React.FunctionComponent<GeneralProps>;
131
  Trigger: React.FunctionComponent<TriggerProps>;
132
}
133
134
const Dialog: React.FunctionComponent<DialogContext> & DialogComposition = (
135
  props,
136
) => {
137
  const { id, className, children, ...rest } = props;
138
  const ref = React.useRef(null);
139
  React.useEffect(() => {
140
    h2DialogResizeOnViewport(ref.current);
141
    h2DialogEnableOpenDialogs(ref.current);
142
  });
143
  return (
144
    <DialogContext.Provider value={props}>
145
      <div
146
        ref={ref}
147
        aria-hidden="true"
148
        aria-describedby={`${id}Content`}
149
        aria-labelledby={`${id}Title`}
150
        data-h2-dialog={id}
151
        tabIndex={-1}
152
        role="dialog"
153
      >
154
        <div data-h2-dialog-wrapper className={className} {...rest}>
155
          {children}
156
        </div>
157
      </div>
158
      <div data-h2-dialog-overlay="black, .9" />
159
    </DialogContext.Provider>
160
  );
161
};
162
163
// We expose the children components here, as properties.
164
// Using the dot notation we explicitly set the composition relationships,
165
// btw the Dialog component and its sub components.
166
Dialog.Actions = Actions;
167
Dialog.ActionBtn = ActionBtn;
168
Dialog.Content = Content;
169
Dialog.Header = Header;
170
Dialog.Title = Title;
171
Dialog.Trigger = Trigger;
172
173
export default Dialog;
174