Passed
Push — dev ( 7e68d0...d35dbf )
by Tristan
05:41 queued 11s
created

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

Complexity

Total Complexity 1
Complexity/F 0

Size

Lines of Code 120
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 1
eloc 89
mnd 1
bc 1
fnc 0
dl 0
loc 120
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
import * as React from "react";
2
import { useIntl, defineMessages } from "react-intl";
3
import { h2ComponentAccordionAddTriggerEvent } from "@hydrogen-design-system/system/dist/import/latest/components/accordion/scripts/accordion";
4
import { GeneralProps, GeneralBtnProps } from "./utils";
5
6
const messages = defineMessages({
7
  expand: {
8
    id: "hydrogen.accordion.expand",
9
    defaultMessage: "Click to view...",
10
    description: " Accessibility text for the accordion expand button.",
11
  },
12
});
13
14
interface AccordionContext {
15
  /** The position of the open and close trigger element, which can be set to the left or right side of the accordion buttons content */
16
  triggerPos?: "left" | "right";
17
}
18
19
const AccordionContext = React.createContext<AccordionContext | undefined>(
20
  undefined,
21
);
22
23
/**
24
 * This Context hook allows our child components to easily reach
25
 * into the Tabs context and get the pieces it needs.
26
 *
27
 * Bonus: it even makes sure the component is used within a
28
 * Accordion component!
29
 */
30
const useAccordionContext = (): {} => {
31
  const context = React.useContext(AccordionContext);
32
  if (!context) {
33
    throw new Error(
34
      "This component must be used within a <Accordion> component.",
35
    );
36
  }
37
  return context;
38
};
39
40
interface BtnProps extends GeneralProps, GeneralBtnProps {
41
  /** The accordion add icon which can be a string or a react element */
42
  addIcon?: React.ReactElement | string;
43
  /** The accordion remove icon which can be a string or a react element */
44
  removeIcon?: React.ReactElement | string;
45
  /** The standard css class attribute */
46
  className?: string;
47
}
48
49
const Btn: React.FunctionComponent<BtnProps> = (props) => {
50
  useAccordionContext(); // Ensures sub-component can only be used within the Accordion component.
51
  const { addIcon, removeIcon, buttonStyling, className, children } = props;
52
  const intl = useIntl();
53
  return (
54
    <button
55
      data-h2-button={buttonStyling}
56
      type="button"
57
      aria-expanded="false"
58
      data-h2-accordion-trigger
59
      tabIndex={0}
60
      className={className}
61
      {...props}
62
    >
63
      <span data-h2-accordion-trigger-label>
64
        {intl.formatMessage(messages.expand)}
65
      </span>
66
      <span aria-hidden="true" data-h2-accordion-add-icon>
67
        {addIcon || <i className="fas fa-plus" />}
68
      </span>
69
      <span aria-hidden="true" data-h2-accordion-remove-icon>
70
        {removeIcon || <i className="fas fa-minus" />}
71
      </span>
72
      <div data-h2-accordion-trigger-content>{children}</div>
73
    </button>
74
  );
75
};
76
77
const Content: React.FunctionComponent<GeneralProps> = (props) => {
78
  useAccordionContext(); // Ensures sub-component can only be used within the Accordion component.
79
  const { className, children } = props;
80
  return (
81
    <div
82
      aria-hidden="true"
83
      data-h2-accordion-content
84
      className={className}
85
      {...props}
86
    >
87
      {children}
88
    </div>
89
  );
90
};
91
92
interface AccordionComposition {
93
  Btn: React.FunctionComponent<BtnProps>;
94
  Content: React.FunctionComponent<GeneralProps>;
95
}
96
97
const Accordion: React.FunctionComponent<AccordionContext> &
98
  AccordionComposition = (props) => {
99
  const { triggerPos, children } = props;
100
  const ref = React.useRef(null);
101
  React.useEffect((): void => {
102
    h2ComponentAccordionAddTriggerEvent("latest", ref.current);
103
  });
104
  return (
105
    <AccordionContext.Provider value={props}>
106
      <div ref={ref} data-h2-accordion={triggerPos || "left"} {...props}>
107
        {children}
108
      </div>
109
    </AccordionContext.Provider>
110
  );
111
};
112
113
// We expose the children components here, as properties.
114
// Using the dot notation we explicitly set the composition relationships,
115
// btw the Accordion component and its sub components.
116
Accordion.Btn = Btn;
117
Accordion.Content = Content;
118
119
export default Accordion;
120