brainly-style-guide
Advanced tools
Comparing version 186.2.0 to 187.0.0
@@ -16,2 +16,6 @@ "use strict"; | ||
var _utils = require("../utils"); | ||
var _invariant = _interopRequireDefault(require("../utils/invariant")); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -33,9 +37,17 @@ | ||
function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } | ||
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } | ||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } | ||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } | ||
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } | ||
function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } | ||
@@ -71,8 +83,55 @@ var KEY_CODES = { | ||
_ref$reduceMotion = _ref.reduceMotion, | ||
reduceMotion = _ref$reduceMotion === void 0 ? false : _ref$reduceMotion; | ||
reduceMotion = _ref$reduceMotion === void 0 ? false : _ref$reduceMotion, | ||
defaultExpanded = _ref.defaultExpanded, | ||
expanded = _ref.expanded, | ||
onChange = _ref.onChange; | ||
var wrapperRef = (0, React.useRef)(null); | ||
var isControlled = expanded !== undefined; | ||
var _useReducer = (0, React.useReducer)(reducer, { | ||
opened: {}, | ||
focusedElementId: null | ||
var _useRef = (0, React.useRef)(isControlled), | ||
wasControlled = _useRef.current; | ||
if (_utils.__DEV__) { | ||
(0, _invariant.default)(!(isControlled && !onChange), // eslint-disable-next-line max-len | ||
' You provided an `expanded` prop to a Accordion without an `onChange` handler. Users won`t be able to switch between expanded/collapsed state.'); | ||
(0, _invariant.default)(!(wasControlled && !isControlled), 'You cannot change Accordion component from controlled to uncontrolled variant.'); | ||
(0, _invariant.default)(!(!wasControlled && isControlled), 'You cannot change Accordion component from uncontrolled to controlled variant.'); | ||
(0, _invariant.default)(!(isControlled && allowMultiple), 'allowMultiple is not working in controlled Accordion'); | ||
(0, _invariant.default)(!(!allowMultiple && Array.isArray(defaultExpanded) && defaultExpanded.length > 1), // eslint-disable-next-line max-len | ||
'defaultExpanded is an array with more than 1 element but allowMultiple prop is not set. The first value from the array was picked as a default expanded. Set allowMultiple attribute or provide only one default expanded.'); | ||
} | ||
var getUpdatedOpenedItems = (0, React.useCallback)(function (expanded, id, value) { | ||
if (value) { | ||
return allowMultiple ? _toConsumableArray(new Set([].concat(_toConsumableArray(expanded), [id]))) : [id]; | ||
} | ||
return allowMultiple ? expanded.filter(function (item) { | ||
return item !== id; | ||
}) : []; | ||
}, [allowMultiple]); | ||
var _useReducer = (0, React.useReducer)(reducer, null, function () { | ||
if (isControlled) { | ||
return { | ||
expanded: [], | ||
focusedElementId: null | ||
}; | ||
} | ||
if (defaultExpanded !== undefined) { | ||
var expandedArray = Array.isArray(defaultExpanded) ? defaultExpanded : [defaultExpanded]; | ||
var newState = expandedArray.filter(function (item, idx) { | ||
return allowMultiple || idx < 1; | ||
}); | ||
return { | ||
expanded: newState, | ||
focusedElementId: null | ||
}; | ||
} | ||
return { | ||
expanded: [], | ||
focusedElementId: null | ||
}; | ||
}), | ||
@@ -96,3 +155,3 @@ _useReducer2 = _slicedToArray(_useReducer, 2), | ||
dispatch({ | ||
type: 'accordion/KEYBOARD_SET_OPENED' | ||
type: 'accordion/KEYBOARD_SET_EXPANDED' | ||
}); | ||
@@ -110,25 +169,19 @@ } | ||
function getUpdatedOpenedItems(opened, id, value) { | ||
return allowMultiple ? _objectSpread(_objectSpread({}, opened), {}, _defineProperty({}, id, value)) : _defineProperty({}, id, value); | ||
} | ||
function reducer(state, action) { | ||
switch (action.type) { | ||
case 'accordion/SET_OPENED': | ||
case 'accordion/SET_EXPANDED': | ||
{ | ||
var _action$payload = action.payload, | ||
id = _action$payload.id, | ||
value = _action$payload.value; | ||
var _expanded = action.payload.expanded; | ||
return _objectSpread(_objectSpread({}, state), {}, { | ||
opened: getUpdatedOpenedItems(state.opened, id, value) | ||
expanded: _expanded | ||
}); | ||
} | ||
case 'accordion/KEYBOARD_SET_OPENED': | ||
case 'accordion/KEYBOARD_SET_EXPANDED': | ||
{ | ||
var opened = state.opened, | ||
var _expanded2 = state.expanded, | ||
focusedElementId = state.focusedElementId; | ||
if (focusedElementId === null) return state; | ||
return _objectSpread(_objectSpread({}, state), {}, { | ||
opened: getUpdatedOpenedItems(state.opened, focusedElementId, !opened[focusedElementId]) | ||
expanded: getUpdatedOpenedItems(state.expanded, focusedElementId, !_expanded2.includes(focusedElementId)) | ||
}); | ||
@@ -149,17 +202,50 @@ } | ||
var _useState = (0, React.useState)(), | ||
_useState2 = _slicedToArray(_useState, 2), | ||
prevExpanded = _useState2[0], | ||
setPrevExpanded = _useState2[1]; | ||
if (isControlled && expanded !== prevExpanded) { | ||
setPrevExpanded(expanded); // expanded || '' is to satisfy flow. | ||
// isControlled flag is true when expanded !== undefined but this condition is not interpreted | ||
// correctly by flow causing type error. Replacing isControlled with expanded !== undefined would work but using isControlled is more clear | ||
var expandedArray = Array.isArray(expanded) ? expanded : [expanded || '']; | ||
dispatch({ | ||
type: 'accordion/SET_EXPANDED', | ||
payload: { | ||
expanded: expandedArray | ||
} | ||
}); | ||
} | ||
var noGapBetweenElements = spacing === 'none'; | ||
var spaceClass = spacing === 'none' ? undefined : spaceClasses[spacing]; | ||
return /*#__PURE__*/React.createElement(AccordionContext.Provider, { | ||
value: { | ||
var onItemSelect = (0, React.useCallback)(function (id, value) { | ||
onChange && onChange(id); | ||
if (!isControlled) { | ||
dispatch({ | ||
type: 'accordion/SET_EXPANDED', | ||
payload: { | ||
expanded: getUpdatedOpenedItems(state.expanded, id, value) | ||
} | ||
}); | ||
} | ||
}, [getUpdatedOpenedItems, isControlled, onChange, state.expanded]); | ||
var context = (0, React.useMemo)(function () { | ||
return { | ||
noGapBetweenElements: noGapBetweenElements, | ||
opened: state.opened, | ||
expanded: state.expanded, | ||
focusedElementId: state.focusedElementId, | ||
dispatch: dispatch, | ||
reduceMotion: hasReduceMotion | ||
} | ||
reduceMotion: hasReduceMotion, | ||
onItemSelect: onItemSelect | ||
}; | ||
}, [hasReduceMotion, noGapBetweenElements, onItemSelect, state.focusedElementId, state.expanded]); | ||
return /*#__PURE__*/React.createElement(AccordionContext.Provider, { | ||
value: context | ||
}, /*#__PURE__*/React.createElement("div", { | ||
ref: wrapperRef, | ||
className: (0, _classnames.default)(spaceClass, className), | ||
"data-allow-multiple": allowMultiple, | ||
"data-allow-toggle": !allowMultiple | ||
className: (0, _classnames.default)(spaceClass, className) | ||
}, children)); | ||
@@ -166,0 +252,0 @@ }; |
@@ -8,3 +8,3 @@ "use strict"; | ||
}); | ||
exports.NoGaps = exports.Default = exports.default = void 0; | ||
exports.Controlled = exports.NoGaps = exports.Default = exports.default = void 0; | ||
@@ -25,2 +25,20 @@ var React = _interopRequireWildcard(require("react")); | ||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } | ||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } | ||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } | ||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } | ||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } | ||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } | ||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } | ||
function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
var _default = { | ||
@@ -40,2 +58,17 @@ title: 'Layout/Accordion', | ||
} | ||
}, | ||
onChange: { | ||
table: { | ||
category: 'Events' | ||
} | ||
}, | ||
defaultExpanded: { | ||
control: { | ||
type: 'array' | ||
} | ||
}, | ||
expanded: { | ||
control: { | ||
type: 'array' | ||
} | ||
} | ||
@@ -86,47 +119,2 @@ } | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(_AccordionItem.default, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(_AccordionItem.default, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(_AccordionItem.default, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(_AccordionItem.default, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(_AccordionItem.default, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(_AccordionItem.default, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(_AccordionItem.default, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(_AccordionItem.default, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(_AccordionItem.default, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
}))); | ||
@@ -137,9 +125,6 @@ }; | ||
var NoGaps = function NoGaps() { | ||
return /*#__PURE__*/React.createElement(_Accordion.default, { | ||
spacing: "none", | ||
allowMultiple: true | ||
}, /*#__PURE__*/React.createElement(_AccordionItem.default, { | ||
var NoGaps = function NoGaps(args) { | ||
return /*#__PURE__*/React.createElement(_Accordion.default, args, /*#__PURE__*/React.createElement(_AccordionItem.default, { | ||
title: copy.title, | ||
defaultOpened: true | ||
id: "first" | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
@@ -150,3 +135,3 @@ url: copy.url, | ||
title: copy.title, | ||
defaultOpened: true | ||
id: "second" | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
@@ -168,2 +153,65 @@ url: copy.url, | ||
exports.NoGaps = NoGaps; | ||
exports.NoGaps = NoGaps; | ||
NoGaps.args = { | ||
spacing: 'none', | ||
allowMultiple: true, | ||
defaultExpanded: ['first', 'second'] | ||
}; | ||
var CONTROLLED_ACCORDION_IDS = ['accrodion item 1', 'accordion item 2', 'accordion item 3']; | ||
var Controlled = function Controlled(args) { | ||
var _React$useState = React.useState(), | ||
_React$useState2 = _slicedToArray(_React$useState, 2), | ||
prevExpanded = _React$useState2[0], | ||
setPrevExpanded = _React$useState2[1]; | ||
var _React$useState3 = React.useState(''), | ||
_React$useState4 = _slicedToArray(_React$useState3, 2), | ||
expanded = _React$useState4[0], | ||
setExpanded = _React$useState4[1]; | ||
var handleChange = function handleChange(id) { | ||
return setExpanded(id); | ||
}; | ||
var propsExpanded = args.expanded, | ||
props = _objectWithoutProperties(args, ["expanded"]); | ||
if (propsExpanded !== prevExpanded) { | ||
setPrevExpanded(propsExpanded); | ||
setExpanded(propsExpanded); | ||
} | ||
return /*#__PURE__*/React.createElement(_Accordion.default, _extends({ | ||
onChange: handleChange, | ||
expanded: expanded | ||
}, props), CONTROLLED_ACCORDION_IDS.map(function (id) { | ||
return /*#__PURE__*/React.createElement(_AccordionItem.default, { | ||
title: copy.title, | ||
key: id, | ||
id: id | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})); | ||
})); | ||
}; | ||
exports.Controlled = Controlled; | ||
Controlled.args = { | ||
expanded: CONTROLLED_ACCORDION_IDS[0] | ||
}; | ||
Controlled.argTypes = { | ||
expanded: { | ||
control: { | ||
type: 'select', | ||
options: CONTROLLED_ACCORDION_IDS | ||
} | ||
}, | ||
allowMultiple: { | ||
control: null | ||
}, | ||
defaultExpanded: { | ||
control: null | ||
} | ||
}; |
@@ -55,12 +55,10 @@ "use strict"; | ||
className = _ref$className === void 0 ? '' : _ref$className, | ||
_ref$defaultOpened = _ref.defaultOpened, | ||
defaultOpened = _ref$defaultOpened === void 0 ? false : _ref$defaultOpened, | ||
_ref$padding = _ref.padding, | ||
padding = _ref$padding === void 0 ? 'm' : _ref$padding, | ||
_ref$tabIndex = _ref.tabIndex, | ||
tabIndex = _ref$tabIndex === void 0 ? 0 : _ref$tabIndex; | ||
var hasRendered = (0, React.useRef)(false); | ||
tabIndex = _ref$tabIndex === void 0 ? 0 : _ref$tabIndex, | ||
customId = _ref.id; | ||
var contentRef = (0, React.useRef)(null); | ||
var _useRef = (0, React.useRef)("AccordionItem_".concat(generateId())), | ||
var _useRef = (0, React.useRef)(customId !== null && customId !== void 0 ? customId : "AccordionItem_".concat(generateId())), | ||
id = _useRef.current; | ||
@@ -72,6 +70,7 @@ | ||
noGapBetweenElements = _useContext.noGapBetweenElements, | ||
opened = _useContext.opened, | ||
expanded = _useContext.expanded, | ||
focusedElementId = _useContext.focusedElementId, | ||
dispatch = _useContext.dispatch, | ||
reduceMotion = _useContext.reduceMotion; | ||
reduceMotion = _useContext.reduceMotion, | ||
onItemSelect = _useContext.onItemSelect; | ||
@@ -83,15 +82,10 @@ var _useState = (0, React.useState)(false), | ||
var isHidden = !opened[id]; | ||
var isCollapsed = !expanded.includes(id); | ||
var isFocused = focusedElementId === id; | ||
var isHighlighted = isHovered || isFocused; | ||
var isBorderHighlighted = isHighlighted && !noGapBetweenElements; | ||
var isTitleString = typeof title === 'string'; | ||
var toggleOpen = function toggleOpen() { | ||
dispatch({ | ||
type: 'accordion/SET_OPENED', | ||
payload: { | ||
id: id, | ||
value: isHidden | ||
} | ||
}); | ||
onItemSelect(id, isCollapsed); | ||
}; | ||
@@ -118,15 +112,2 @@ | ||
(0, React.useEffect)(function () { | ||
if (defaultOpened) { | ||
dispatch({ | ||
type: 'accordion/SET_OPENED', | ||
payload: { | ||
id: id, | ||
value: true | ||
} | ||
}); | ||
} | ||
hasRendered.current = true; //eslint-disable-next-line | ||
}, []); | ||
(0, React.useLayoutEffect)(function () { | ||
var content = contentRef.current; | ||
@@ -193,4 +174,3 @@ | ||
if (hasRendered.current === false) return; | ||
isHidden ? collapse() : expand(); | ||
isCollapsed ? collapse() : expand(); | ||
return function () { | ||
@@ -203,3 +183,3 @@ if (!content) { | ||
}; | ||
}, [isHidden, reduceMotion]); | ||
}, [isCollapsed, reduceMotion]); | ||
return /*#__PURE__*/React.createElement(_Box.default, { | ||
@@ -227,3 +207,3 @@ color: "light", | ||
onBlur: handleBlur, | ||
"aria-expanded": !isHidden, | ||
"aria-expanded": !isCollapsed, | ||
"aria-controls": contentId, | ||
@@ -237,3 +217,3 @@ id: id, | ||
alignItems: "center" | ||
}, /*#__PURE__*/React.createElement(_Link.default, { | ||
}, isTitleString ? /*#__PURE__*/React.createElement(_Link.default, { | ||
size: titleSize, | ||
@@ -243,2 +223,4 @@ color: "black", | ||
underlined: isHighlighted | ||
}, title) : /*#__PURE__*/React.createElement("span", { | ||
className: "sg-accordion-item__title" | ||
}, title), /*#__PURE__*/React.createElement(_Flex.default, { | ||
@@ -254,3 +236,3 @@ justifyContent: "center", | ||
className: (0, _classnames.default)('sg-accordion-item__arrow', { | ||
'sg-accordion-item__arrow--visible': !isHidden | ||
'sg-accordion-item__arrow--visible': !isCollapsed | ||
}) | ||
@@ -257,0 +239,0 @@ })))), /*#__PURE__*/React.createElement("div", { |
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty"; | ||
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; | ||
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray"; | ||
@@ -9,7 +10,9 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } | ||
// eslint-disable-next-line import/no-duplicates | ||
import * as React from 'react'; // eslint-disable-next-line import/no-duplicates | ||
import { createContext, useReducer, useEffect, useRef } from 'react'; | ||
import * as React from 'react'; | ||
import { createContext, useReducer, useEffect, useRef, useCallback, useMemo, useState // eslint-disable-next-line import/no-duplicates | ||
} from 'react'; | ||
import cx from 'classnames'; | ||
import useReducedMotion from '../utils/useReducedMotion'; | ||
import { __DEV__ } from '../utils'; | ||
import invariant from '../utils/invariant'; | ||
export var KEY_CODES = { | ||
@@ -41,8 +44,55 @@ '32': 'space', | ||
_ref$reduceMotion = _ref.reduceMotion, | ||
reduceMotion = _ref$reduceMotion === void 0 ? false : _ref$reduceMotion; | ||
reduceMotion = _ref$reduceMotion === void 0 ? false : _ref$reduceMotion, | ||
defaultExpanded = _ref.defaultExpanded, | ||
expanded = _ref.expanded, | ||
onChange = _ref.onChange; | ||
var wrapperRef = useRef(null); | ||
var isControlled = expanded !== undefined; | ||
var _useReducer = useReducer(reducer, { | ||
opened: {}, | ||
focusedElementId: null | ||
var _useRef = useRef(isControlled), | ||
wasControlled = _useRef.current; | ||
if (__DEV__) { | ||
invariant(!(isControlled && !onChange), // eslint-disable-next-line max-len | ||
' You provided an `expanded` prop to a Accordion without an `onChange` handler. Users won`t be able to switch between expanded/collapsed state.'); | ||
invariant(!(wasControlled && !isControlled), 'You cannot change Accordion component from controlled to uncontrolled variant.'); | ||
invariant(!(!wasControlled && isControlled), 'You cannot change Accordion component from uncontrolled to controlled variant.'); | ||
invariant(!(isControlled && allowMultiple), 'allowMultiple is not working in controlled Accordion'); | ||
invariant(!(!allowMultiple && Array.isArray(defaultExpanded) && defaultExpanded.length > 1), // eslint-disable-next-line max-len | ||
'defaultExpanded is an array with more than 1 element but allowMultiple prop is not set. The first value from the array was picked as a default expanded. Set allowMultiple attribute or provide only one default expanded.'); | ||
} | ||
var getUpdatedOpenedItems = useCallback(function (expanded, id, value) { | ||
if (value) { | ||
return allowMultiple ? _toConsumableArray(new Set([].concat(_toConsumableArray(expanded), [id]))) : [id]; | ||
} | ||
return allowMultiple ? expanded.filter(function (item) { | ||
return item !== id; | ||
}) : []; | ||
}, [allowMultiple]); | ||
var _useReducer = useReducer(reducer, null, function () { | ||
if (isControlled) { | ||
return { | ||
expanded: [], | ||
focusedElementId: null | ||
}; | ||
} | ||
if (defaultExpanded !== undefined) { | ||
var expandedArray = Array.isArray(defaultExpanded) ? defaultExpanded : [defaultExpanded]; | ||
var newState = expandedArray.filter(function (item, idx) { | ||
return allowMultiple || idx < 1; | ||
}); | ||
return { | ||
expanded: newState, | ||
focusedElementId: null | ||
}; | ||
} | ||
return { | ||
expanded: [], | ||
focusedElementId: null | ||
}; | ||
}), | ||
@@ -66,3 +116,3 @@ _useReducer2 = _slicedToArray(_useReducer, 2), | ||
dispatch({ | ||
type: 'accordion/KEYBOARD_SET_OPENED' | ||
type: 'accordion/KEYBOARD_SET_EXPANDED' | ||
}); | ||
@@ -80,25 +130,19 @@ } | ||
function getUpdatedOpenedItems(opened, id, value) { | ||
return allowMultiple ? _objectSpread(_objectSpread({}, opened), {}, _defineProperty({}, id, value)) : _defineProperty({}, id, value); | ||
} | ||
function reducer(state, action) { | ||
switch (action.type) { | ||
case 'accordion/SET_OPENED': | ||
case 'accordion/SET_EXPANDED': | ||
{ | ||
var _action$payload = action.payload, | ||
id = _action$payload.id, | ||
value = _action$payload.value; | ||
var _expanded = action.payload.expanded; | ||
return _objectSpread(_objectSpread({}, state), {}, { | ||
opened: getUpdatedOpenedItems(state.opened, id, value) | ||
expanded: _expanded | ||
}); | ||
} | ||
case 'accordion/KEYBOARD_SET_OPENED': | ||
case 'accordion/KEYBOARD_SET_EXPANDED': | ||
{ | ||
var opened = state.opened, | ||
var _expanded2 = state.expanded, | ||
focusedElementId = state.focusedElementId; | ||
if (focusedElementId === null) return state; | ||
return _objectSpread(_objectSpread({}, state), {}, { | ||
opened: getUpdatedOpenedItems(state.opened, focusedElementId, !opened[focusedElementId]) | ||
expanded: getUpdatedOpenedItems(state.expanded, focusedElementId, !_expanded2.includes(focusedElementId)) | ||
}); | ||
@@ -119,17 +163,50 @@ } | ||
var _useState = useState(), | ||
_useState2 = _slicedToArray(_useState, 2), | ||
prevExpanded = _useState2[0], | ||
setPrevExpanded = _useState2[1]; | ||
if (isControlled && expanded !== prevExpanded) { | ||
setPrevExpanded(expanded); // expanded || '' is to satisfy flow. | ||
// isControlled flag is true when expanded !== undefined but this condition is not interpreted | ||
// correctly by flow causing type error. Replacing isControlled with expanded !== undefined would work but using isControlled is more clear | ||
var expandedArray = Array.isArray(expanded) ? expanded : [expanded || '']; | ||
dispatch({ | ||
type: 'accordion/SET_EXPANDED', | ||
payload: { | ||
expanded: expandedArray | ||
} | ||
}); | ||
} | ||
var noGapBetweenElements = spacing === 'none'; | ||
var spaceClass = spacing === 'none' ? undefined : spaceClasses[spacing]; | ||
return /*#__PURE__*/React.createElement(AccordionContext.Provider, { | ||
value: { | ||
var onItemSelect = useCallback(function (id, value) { | ||
onChange && onChange(id); | ||
if (!isControlled) { | ||
dispatch({ | ||
type: 'accordion/SET_EXPANDED', | ||
payload: { | ||
expanded: getUpdatedOpenedItems(state.expanded, id, value) | ||
} | ||
}); | ||
} | ||
}, [getUpdatedOpenedItems, isControlled, onChange, state.expanded]); | ||
var context = useMemo(function () { | ||
return { | ||
noGapBetweenElements: noGapBetweenElements, | ||
opened: state.opened, | ||
expanded: state.expanded, | ||
focusedElementId: state.focusedElementId, | ||
dispatch: dispatch, | ||
reduceMotion: hasReduceMotion | ||
} | ||
reduceMotion: hasReduceMotion, | ||
onItemSelect: onItemSelect | ||
}; | ||
}, [hasReduceMotion, noGapBetweenElements, onItemSelect, state.focusedElementId, state.expanded]); | ||
return /*#__PURE__*/React.createElement(AccordionContext.Provider, { | ||
value: context | ||
}, /*#__PURE__*/React.createElement("div", { | ||
ref: wrapperRef, | ||
className: cx(spaceClass, className), | ||
"data-allow-multiple": allowMultiple, | ||
"data-allow-toggle": !allowMultiple | ||
className: cx(spaceClass, className) | ||
}, children)); | ||
@@ -136,0 +213,0 @@ }; |
@@ -0,1 +1,4 @@ | ||
import _extends from "@babel/runtime/helpers/esm/extends"; | ||
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties"; | ||
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; | ||
import * as React from 'react'; | ||
@@ -19,2 +22,17 @@ import Accordion from './Accordion'; | ||
} | ||
}, | ||
onChange: { | ||
table: { | ||
category: 'Events' | ||
} | ||
}, | ||
defaultExpanded: { | ||
control: { | ||
type: 'array' | ||
} | ||
}, | ||
expanded: { | ||
control: { | ||
type: 'array' | ||
} | ||
} | ||
@@ -64,56 +82,8 @@ } | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(AccordionItem, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(AccordionItem, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(AccordionItem, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(AccordionItem, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(AccordionItem, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(AccordionItem, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(AccordionItem, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(AccordionItem, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})), /*#__PURE__*/React.createElement(AccordionItem, { | ||
title: copy.title | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
}))); | ||
}; | ||
export var NoGaps = function NoGaps() { | ||
return /*#__PURE__*/React.createElement(Accordion, { | ||
spacing: "none", | ||
allowMultiple: true | ||
}, /*#__PURE__*/React.createElement(AccordionItem, { | ||
export var NoGaps = function NoGaps(args) { | ||
return /*#__PURE__*/React.createElement(Accordion, args, /*#__PURE__*/React.createElement(AccordionItem, { | ||
title: copy.title, | ||
defaultOpened: true | ||
id: "first" | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
@@ -124,3 +94,3 @@ url: copy.url, | ||
title: copy.title, | ||
defaultOpened: true | ||
id: "second" | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
@@ -140,2 +110,62 @@ url: copy.url, | ||
}))); | ||
}; | ||
NoGaps.args = { | ||
spacing: 'none', | ||
allowMultiple: true, | ||
defaultExpanded: ['first', 'second'] | ||
}; | ||
var CONTROLLED_ACCORDION_IDS = ['accrodion item 1', 'accordion item 2', 'accordion item 3']; | ||
export var Controlled = function Controlled(args) { | ||
var _React$useState = React.useState(), | ||
_React$useState2 = _slicedToArray(_React$useState, 2), | ||
prevExpanded = _React$useState2[0], | ||
setPrevExpanded = _React$useState2[1]; | ||
var _React$useState3 = React.useState(''), | ||
_React$useState4 = _slicedToArray(_React$useState3, 2), | ||
expanded = _React$useState4[0], | ||
setExpanded = _React$useState4[1]; | ||
var handleChange = function handleChange(id) { | ||
return setExpanded(id); | ||
}; | ||
var propsExpanded = args.expanded, | ||
props = _objectWithoutProperties(args, ["expanded"]); | ||
if (propsExpanded !== prevExpanded) { | ||
setPrevExpanded(propsExpanded); | ||
setExpanded(propsExpanded); | ||
} | ||
return /*#__PURE__*/React.createElement(Accordion, _extends({ | ||
onChange: handleChange, | ||
expanded: expanded | ||
}, props), CONTROLLED_ACCORDION_IDS.map(function (id) { | ||
return /*#__PURE__*/React.createElement(AccordionItem, { | ||
title: copy.title, | ||
key: id, | ||
id: id | ||
}, copy.description, " ", /*#__PURE__*/React.createElement(CallToAction, { | ||
url: copy.url, | ||
cta: copy.cta | ||
})); | ||
})); | ||
}; | ||
Controlled.args = { | ||
expanded: CONTROLLED_ACCORDION_IDS[0] | ||
}; | ||
Controlled.argTypes = { | ||
expanded: { | ||
control: { | ||
type: 'select', | ||
options: CONTROLLED_ACCORDION_IDS | ||
} | ||
}, | ||
allowMultiple: { | ||
control: null | ||
}, | ||
defaultExpanded: { | ||
control: null | ||
} | ||
}; |
@@ -5,3 +5,3 @@ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; | ||
import { useContext, useLayoutEffect, useEffect, useRef, useState } from 'react'; | ||
import { useContext, useEffect, useRef, useState } from 'react'; | ||
import cx from 'classnames'; | ||
@@ -26,12 +26,10 @@ import Box from '../box/Box'; | ||
className = _ref$className === void 0 ? '' : _ref$className, | ||
_ref$defaultOpened = _ref.defaultOpened, | ||
defaultOpened = _ref$defaultOpened === void 0 ? false : _ref$defaultOpened, | ||
_ref$padding = _ref.padding, | ||
padding = _ref$padding === void 0 ? 'm' : _ref$padding, | ||
_ref$tabIndex = _ref.tabIndex, | ||
tabIndex = _ref$tabIndex === void 0 ? 0 : _ref$tabIndex; | ||
var hasRendered = useRef(false); | ||
tabIndex = _ref$tabIndex === void 0 ? 0 : _ref$tabIndex, | ||
customId = _ref.id; | ||
var contentRef = useRef(null); | ||
var _useRef = useRef("AccordionItem_".concat(generateId())), | ||
var _useRef = useRef(customId !== null && customId !== void 0 ? customId : "AccordionItem_".concat(generateId())), | ||
id = _useRef.current; | ||
@@ -43,6 +41,7 @@ | ||
noGapBetweenElements = _useContext.noGapBetweenElements, | ||
opened = _useContext.opened, | ||
expanded = _useContext.expanded, | ||
focusedElementId = _useContext.focusedElementId, | ||
dispatch = _useContext.dispatch, | ||
reduceMotion = _useContext.reduceMotion; | ||
reduceMotion = _useContext.reduceMotion, | ||
onItemSelect = _useContext.onItemSelect; | ||
@@ -54,15 +53,10 @@ var _useState = useState(false), | ||
var isHidden = !opened[id]; | ||
var isCollapsed = !expanded.includes(id); | ||
var isFocused = focusedElementId === id; | ||
var isHighlighted = isHovered || isFocused; | ||
var isBorderHighlighted = isHighlighted && !noGapBetweenElements; | ||
var isTitleString = typeof title === 'string'; | ||
var toggleOpen = function toggleOpen() { | ||
dispatch({ | ||
type: 'accordion/SET_OPENED', | ||
payload: { | ||
id: id, | ||
value: isHidden | ||
} | ||
}); | ||
onItemSelect(id, isCollapsed); | ||
}; | ||
@@ -89,15 +83,2 @@ | ||
useEffect(function () { | ||
if (defaultOpened) { | ||
dispatch({ | ||
type: 'accordion/SET_OPENED', | ||
payload: { | ||
id: id, | ||
value: true | ||
} | ||
}); | ||
} | ||
hasRendered.current = true; //eslint-disable-next-line | ||
}, []); | ||
useLayoutEffect(function () { | ||
var content = contentRef.current; | ||
@@ -164,4 +145,3 @@ | ||
if (hasRendered.current === false) return; | ||
isHidden ? collapse() : expand(); | ||
isCollapsed ? collapse() : expand(); | ||
return function () { | ||
@@ -174,3 +154,3 @@ if (!content) { | ||
}; | ||
}, [isHidden, reduceMotion]); | ||
}, [isCollapsed, reduceMotion]); | ||
return /*#__PURE__*/React.createElement(Box, { | ||
@@ -198,3 +178,3 @@ color: "light", | ||
onBlur: handleBlur, | ||
"aria-expanded": !isHidden, | ||
"aria-expanded": !isCollapsed, | ||
"aria-controls": contentId, | ||
@@ -208,3 +188,3 @@ id: id, | ||
alignItems: "center" | ||
}, /*#__PURE__*/React.createElement(Link, { | ||
}, isTitleString ? /*#__PURE__*/React.createElement(Link, { | ||
size: titleSize, | ||
@@ -214,2 +194,4 @@ color: "black", | ||
underlined: isHighlighted | ||
}, title) : /*#__PURE__*/React.createElement("span", { | ||
className: "sg-accordion-item__title" | ||
}, title), /*#__PURE__*/React.createElement(Flex, { | ||
@@ -225,3 +207,3 @@ justifyContent: "center", | ||
className: cx('sg-accordion-item__arrow', { | ||
'sg-accordion-item__arrow--visible': !isHidden | ||
'sg-accordion-item__arrow--visible': !isCollapsed | ||
}) | ||
@@ -228,0 +210,0 @@ })))), /*#__PURE__*/React.createElement("div", { |
{ | ||
"name": "brainly-style-guide", | ||
"version": "186.2.0", | ||
"version": "187.0.0", | ||
"description": "Brainly Front-End Style Guide", | ||
@@ -5,0 +5,0 @@ "repository": "https://github.com/brainly/style-guide.git", |
@@ -5,6 +5,16 @@ //@flow strict | ||
import * as React from 'react'; | ||
// eslint-disable-next-line import/no-duplicates | ||
import {createContext, useReducer, useEffect, useRef} from 'react'; | ||
import { | ||
createContext, | ||
useReducer, | ||
useEffect, | ||
useRef, | ||
useCallback, | ||
useMemo, | ||
useState, | ||
// eslint-disable-next-line import/no-duplicates | ||
} from 'react'; | ||
import cx from 'classnames'; | ||
import useReducedMotion from '../utils/useReducedMotion'; | ||
import {__DEV__} from '../utils'; | ||
import invariant from '../utils/invariant'; | ||
@@ -16,9 +26,6 @@ export const KEY_CODES = { | ||
type OpenedItemsType = { | ||
[string]: boolean, | ||
..., | ||
}; | ||
type ExpandedItemsType = Array<string>; | ||
type StateType = $ReadOnly<{ | ||
opened: OpenedItemsType, | ||
expanded: ExpandedItemsType, | ||
focusedElementId: string | null, | ||
@@ -29,6 +36,6 @@ }>; | ||
| { | ||
type: 'accordion/SET_OPENED', | ||
payload: {id: string, value: boolean}, | ||
type: 'accordion/SET_EXPANDED', | ||
payload: {expanded: ExpandedItemsType}, | ||
} | ||
| {type: 'accordion/KEYBOARD_SET_OPENED'} | ||
| {type: 'accordion/KEYBOARD_SET_EXPANDED'} | ||
| { | ||
@@ -55,2 +62,5 @@ type: 'accordion/SET_FOCUSED', | ||
reduceMotion?: boolean, | ||
expanded?: string | Array<string>, | ||
defaultExpanded?: string | Array<string>, | ||
onChange?: string => void, | ||
}>; | ||
@@ -60,6 +70,7 @@ | ||
noGapBetweenElements: boolean, | ||
opened: OpenedItemsType, | ||
expanded: ExpandedItemsType, | ||
focusedElementId: string | null, | ||
dispatch: (action: ActionType) => void, | ||
reduceMotion: boolean, | ||
onItemSelect: (id: string, value: boolean) => void, | ||
... | ||
@@ -88,7 +99,80 @@ }; | ||
reduceMotion = false, | ||
defaultExpanded, | ||
expanded, | ||
onChange, | ||
}: AccordionPropsType) => { | ||
const wrapperRef = useRef<HTMLDivElement | null>(null); | ||
const [state, dispatch] = useReducer(reducer, { | ||
opened: {}, | ||
focusedElementId: null, | ||
const isControlled = expanded !== undefined; | ||
const {current: wasControlled} = useRef<boolean>(isControlled); | ||
if (__DEV__) { | ||
invariant( | ||
!(isControlled && !onChange), | ||
// eslint-disable-next-line max-len | ||
' You provided an `expanded` prop to a Accordion without an `onChange` handler. Users won`t be able to switch between expanded/collapsed state.' | ||
); | ||
invariant( | ||
!(wasControlled && !isControlled), | ||
'You cannot change Accordion component from controlled to uncontrolled variant.' | ||
); | ||
invariant( | ||
!(!wasControlled && isControlled), | ||
'You cannot change Accordion component from uncontrolled to controlled variant.' | ||
); | ||
invariant( | ||
!(isControlled && allowMultiple), | ||
'allowMultiple is not working in controlled Accordion' | ||
); | ||
invariant( | ||
!( | ||
!allowMultiple && | ||
Array.isArray(defaultExpanded) && | ||
defaultExpanded.length > 1 | ||
), | ||
// eslint-disable-next-line max-len | ||
'defaultExpanded is an array with more than 1 element but allowMultiple prop is not set. The first value from the array was picked as a default expanded. Set allowMultiple attribute or provide only one default expanded.' | ||
); | ||
} | ||
const getUpdatedOpenedItems = useCallback( | ||
(expanded: ExpandedItemsType, id: string, value: boolean) => { | ||
if (value) { | ||
return allowMultiple ? [...new Set([...expanded, id])] : [id]; | ||
} | ||
return allowMultiple ? expanded.filter(item => item !== id) : []; | ||
}, | ||
[allowMultiple] | ||
); | ||
const [state, dispatch] = useReducer(reducer, null, () => { | ||
if (isControlled) { | ||
return { | ||
expanded: [], | ||
focusedElementId: null, | ||
}; | ||
} | ||
if (defaultExpanded !== undefined) { | ||
const expandedArray = Array.isArray(defaultExpanded) | ||
? defaultExpanded | ||
: [defaultExpanded]; | ||
const newState = expandedArray.filter( | ||
(item, idx) => allowMultiple || idx < 1 | ||
); | ||
return { | ||
expanded: newState, | ||
focusedElementId: null, | ||
}; | ||
} | ||
return { | ||
expanded: [], | ||
focusedElementId: null, | ||
}; | ||
}); | ||
@@ -111,3 +195,3 @@ const hasReduceMotion = useReducedMotion() || reduceMotion; | ||
dispatch({type: 'accordion/KEYBOARD_SET_OPENED'}); | ||
dispatch({type: 'accordion/KEYBOARD_SET_EXPANDED'}); | ||
} | ||
@@ -125,23 +209,15 @@ } | ||
function getUpdatedOpenedItems( | ||
opened: OpenedItemsType, | ||
id: string, | ||
value: boolean | ||
) { | ||
return allowMultiple ? {...opened, [id]: value} : {[id]: value}; | ||
} | ||
function reducer(state: StateType, action: ActionType): StateType { | ||
switch (action.type) { | ||
case 'accordion/SET_OPENED': { | ||
const {id, value} = action.payload; | ||
case 'accordion/SET_EXPANDED': { | ||
const {expanded} = action.payload; | ||
return { | ||
...state, | ||
opened: getUpdatedOpenedItems(state.opened, id, value), | ||
expanded, | ||
}; | ||
} | ||
case 'accordion/KEYBOARD_SET_OPENED': { | ||
const {opened, focusedElementId} = state; | ||
case 'accordion/KEYBOARD_SET_EXPANDED': { | ||
const {expanded, focusedElementId} = state; | ||
@@ -152,6 +228,6 @@ if (focusedElementId === null) return state; | ||
...state, | ||
opened: getUpdatedOpenedItems( | ||
state.opened, | ||
expanded: getUpdatedOpenedItems( | ||
state.expanded, | ||
focusedElementId, | ||
!opened[focusedElementId] | ||
!expanded.includes(focusedElementId) | ||
), | ||
@@ -173,21 +249,58 @@ }; | ||
const [prevExpanded, setPrevExpanded] = useState(); | ||
if (isControlled && expanded !== prevExpanded) { | ||
setPrevExpanded(expanded); | ||
// expanded || '' is to satisfy flow. | ||
// isControlled flag is true when expanded !== undefined but this condition is not interpreted | ||
// correctly by flow causing type error. Replacing isControlled with expanded !== undefined would work but using isControlled is more clear | ||
const expandedArray = Array.isArray(expanded) ? expanded : [expanded || '']; | ||
dispatch({ | ||
type: 'accordion/SET_EXPANDED', | ||
payload: {expanded: expandedArray}, | ||
}); | ||
} | ||
const noGapBetweenElements = spacing === 'none'; | ||
const spaceClass = spacing === 'none' ? undefined : spaceClasses[spacing]; | ||
const onItemSelect = useCallback( | ||
(id, value) => { | ||
onChange && onChange(id); | ||
if (!isControlled) { | ||
dispatch({ | ||
type: 'accordion/SET_EXPANDED', | ||
payload: { | ||
expanded: getUpdatedOpenedItems(state.expanded, id, value), | ||
}, | ||
}); | ||
} | ||
}, | ||
[getUpdatedOpenedItems, isControlled, onChange, state.expanded] | ||
); | ||
const context = useMemo( | ||
() => ({ | ||
noGapBetweenElements, | ||
expanded: state.expanded, | ||
focusedElementId: state.focusedElementId, | ||
dispatch, | ||
reduceMotion: hasReduceMotion, | ||
onItemSelect, | ||
}), | ||
[ | ||
hasReduceMotion, | ||
noGapBetweenElements, | ||
onItemSelect, | ||
state.focusedElementId, | ||
state.expanded, | ||
] | ||
); | ||
return ( | ||
<AccordionContext.Provider | ||
value={{ | ||
noGapBetweenElements, | ||
opened: state.opened, | ||
focusedElementId: state.focusedElementId, | ||
dispatch, | ||
reduceMotion: hasReduceMotion, | ||
}} | ||
> | ||
<div | ||
ref={wrapperRef} | ||
className={cx(spaceClass, className)} | ||
data-allow-multiple={allowMultiple} | ||
data-allow-toggle={!allowMultiple} | ||
> | ||
<AccordionContext.Provider value={context}> | ||
<div ref={wrapperRef} className={cx(spaceClass, className)}> | ||
{children} | ||
@@ -194,0 +307,0 @@ </div> |
@@ -5,2 +5,3 @@ import * as React from 'react'; | ||
import AccordionItem from './AccordionItem'; | ||
import Link from '../text/Link'; | ||
@@ -151,9 +152,9 @@ describe('<Accordion>', () => { | ||
it('by default expands items that have "defaultOpened" prop', () => { | ||
it('by default expands items that have "defaultExpanded" prop', () => { | ||
const accordion = mount( | ||
<Accordion allowMultiple> | ||
<AccordionItem title="Item 1" defaultOpened> | ||
<Accordion allowMultiple defaultExpanded={['id-1', 'id-2']}> | ||
<AccordionItem title="Item 1" id="id-1"> | ||
Accordion Item Description | ||
</AccordionItem> | ||
<AccordionItem title="Item 2" defaultOpened> | ||
<AccordionItem title="Item 2" id="id-2"> | ||
Accordion Item Description | ||
@@ -168,2 +169,85 @@ </AccordionItem> | ||
}); | ||
it('expands controlled items when expanded is type of array', () => { | ||
const accordionIds = ['id-1', 'id-2', 'id-3']; | ||
const accordion = mount( | ||
<Accordion expanded={accordionIds} onChange={() => undefined}> | ||
{accordionIds.map(id => ( | ||
<AccordionItem title={id} id={id} key={id}> | ||
Accordion Item Description | ||
</AccordionItem> | ||
))} | ||
</Accordion> | ||
); | ||
expect(accordion.find('[aria-labelledby]').hostNodes()).toHaveLength( | ||
accordionIds.length | ||
); | ||
expect(accordion.find('[aria-expanded=true]').hostNodes()).toHaveLength(3); | ||
}); | ||
it('expands controlled item when expanded is type of string', () => { | ||
const accordionIds = ['id-1', 'id-2', 'id-3']; | ||
const accordion = mount( | ||
<Accordion expanded={accordionIds[0]} onChange={noop => noop}> | ||
{accordionIds.map(id => ( | ||
<AccordionItem title={id} id={id} key={id}> | ||
Accordion Item Description | ||
</AccordionItem> | ||
))} | ||
</Accordion> | ||
); | ||
expect(accordion.find('[aria-labelledby]').hostNodes()).toHaveLength( | ||
accordionIds.length | ||
); | ||
expect(accordion.find('[aria-expanded=true]').hostNodes()).toHaveLength(1); | ||
}); | ||
it('calls callback when cliking on item', () => { | ||
const accordionIds = ['id-1', 'id-2', 'id-3']; | ||
const handleOnChange = jest.fn(); | ||
const accordion = mount( | ||
<Accordion expanded={accordionIds[0]} onChange={handleOnChange}> | ||
{accordionIds.map(id => ( | ||
<AccordionItem title={id} id={id} key={id}> | ||
Accordion Item Description | ||
</AccordionItem> | ||
))} | ||
</Accordion> | ||
); | ||
const item = accordionIds[0]; | ||
accordion | ||
.find({title: item}) | ||
.find({role: 'button'}) | ||
.hostNodes() | ||
.simulate('click'); | ||
expect(handleOnChange).toHaveBeenCalled(); | ||
expect(handleOnChange).toHaveBeenCalledWith(item); | ||
}); | ||
it('renders Link when title is string', () => { | ||
const accordion = mount( | ||
<Accordion> | ||
<AccordionItem title="Item 1">Accordion Item Description</AccordionItem> | ||
</Accordion> | ||
); | ||
expect(accordion.find(Link).exists()).toBe(true); | ||
}); | ||
it('does not render Link when title is not string', () => { | ||
const accordion = mount( | ||
<Accordion> | ||
<AccordionItem title={<div>info</div>}> | ||
Accordion Item Description | ||
</AccordionItem> | ||
</Accordion> | ||
); | ||
expect(accordion.find(Link).exists()).toBe(false); | ||
}); | ||
}); |
@@ -21,2 +21,17 @@ //@flow | ||
}, | ||
onChange: { | ||
table: { | ||
category: 'Events', | ||
}, | ||
}, | ||
defaultExpanded: { | ||
control: { | ||
type: 'array', | ||
}, | ||
}, | ||
expanded: { | ||
control: { | ||
type: 'array', | ||
}, | ||
}, | ||
}, | ||
@@ -58,38 +73,11 @@ }; | ||
</AccordionItem> | ||
<AccordionItem title={copy.title}> | ||
{copy.description} <CallToAction url={copy.url} cta={copy.cta} /> | ||
</AccordionItem> | ||
<AccordionItem title={copy.title}> | ||
{copy.description} <CallToAction url={copy.url} cta={copy.cta} /> | ||
</AccordionItem> | ||
<AccordionItem title={copy.title}> | ||
{copy.description} <CallToAction url={copy.url} cta={copy.cta} /> | ||
</AccordionItem> | ||
<AccordionItem title={copy.title}> | ||
{copy.description} <CallToAction url={copy.url} cta={copy.cta} /> | ||
</AccordionItem> | ||
<AccordionItem title={copy.title}> | ||
{copy.description} <CallToAction url={copy.url} cta={copy.cta} /> | ||
</AccordionItem> | ||
<AccordionItem title={copy.title}> | ||
{copy.description} <CallToAction url={copy.url} cta={copy.cta} /> | ||
</AccordionItem> | ||
<AccordionItem title={copy.title}> | ||
{copy.description} <CallToAction url={copy.url} cta={copy.cta} /> | ||
</AccordionItem> | ||
<AccordionItem title={copy.title}> | ||
{copy.description} <CallToAction url={copy.url} cta={copy.cta} /> | ||
</AccordionItem> | ||
<AccordionItem title={copy.title}> | ||
{copy.description} <CallToAction url={copy.url} cta={copy.cta} /> | ||
</AccordionItem> | ||
</Accordion> | ||
); | ||
export const NoGaps = () => ( | ||
<Accordion spacing="none" allowMultiple> | ||
<AccordionItem title={copy.title} defaultOpened> | ||
export const NoGaps = (args: any) => ( | ||
<Accordion {...args}> | ||
<AccordionItem title={copy.title} id="first"> | ||
{copy.description} <CallToAction url={copy.url} cta={copy.cta} /> | ||
</AccordionItem> | ||
<AccordionItem title={copy.title} defaultOpened> | ||
<AccordionItem title={copy.title} id="second"> | ||
{copy.description} <CallToAction url={copy.url} cta={copy.cta} /> | ||
@@ -105,1 +93,48 @@ </AccordionItem> | ||
); | ||
NoGaps.args = { | ||
spacing: 'none', | ||
allowMultiple: true, | ||
defaultExpanded: ['first', 'second'], | ||
}; | ||
const CONTROLLED_ACCORDION_IDS = [ | ||
'accrodion item 1', | ||
'accordion item 2', | ||
'accordion item 3', | ||
]; | ||
export const Controlled = (args: any) => { | ||
const [prevExpanded, setPrevExpanded] = React.useState(); | ||
const [expanded, setExpanded] = React.useState(''); | ||
const handleChange = id => setExpanded(id); | ||
const {expanded: propsExpanded, ...props} = args; | ||
if (propsExpanded !== prevExpanded) { | ||
setPrevExpanded(propsExpanded); | ||
setExpanded(propsExpanded); | ||
} | ||
return ( | ||
<Accordion onChange={handleChange} expanded={expanded} {...props}> | ||
{CONTROLLED_ACCORDION_IDS.map(id => ( | ||
<AccordionItem title={copy.title} key={id} id={id}> | ||
{copy.description} <CallToAction url={copy.url} cta={copy.cta} /> | ||
</AccordionItem> | ||
))} | ||
</Accordion> | ||
); | ||
}; | ||
Controlled.args = { | ||
expanded: CONTROLLED_ACCORDION_IDS[0], | ||
}; | ||
Controlled.argTypes = { | ||
expanded: { | ||
control: {type: 'select', options: CONTROLLED_ACCORDION_IDS}, | ||
}, | ||
allowMultiple: {control: null}, | ||
defaultExpanded: {control: null}, | ||
}; |
@@ -6,3 +6,3 @@ //@flow strict | ||
// eslint-disable-next-line import/no-duplicates | ||
import {useContext, useLayoutEffect, useEffect, useRef, useState} from 'react'; | ||
import {useContext, useEffect, useRef, useState} from 'react'; | ||
@@ -24,5 +24,5 @@ import cx from 'classnames'; | ||
className?: string, | ||
defaultOpened?: boolean, | ||
padding?: PaddingType, | ||
tabIndex?: number, | ||
id?: string, | ||
}>; | ||
@@ -41,9 +41,10 @@ | ||
className = '', | ||
defaultOpened = false, | ||
padding = 'm', | ||
tabIndex = 0, | ||
id: customId, | ||
}: AccordionItemPropsType) => { | ||
const hasRendered = useRef(false); | ||
const contentRef = useRef<HTMLDivElement | null>(null); | ||
const {current: id} = useRef<string>(`AccordionItem_${generateId()}`); | ||
const {current: id} = useRef<string>( | ||
customId ?? `AccordionItem_${generateId()}` | ||
); | ||
const contentId = `Section_${id}`; | ||
@@ -53,19 +54,18 @@ | ||
noGapBetweenElements, | ||
opened, | ||
expanded, | ||
focusedElementId, | ||
dispatch, | ||
reduceMotion, | ||
onItemSelect, | ||
} = useContext(AccordionContext); | ||
const [isHovered, setIsHovered] = useState(false); | ||
const isHidden = !opened[id]; | ||
const isCollapsed = !expanded.includes(id); | ||
const isFocused = focusedElementId === id; | ||
const isHighlighted = isHovered || isFocused; | ||
const isBorderHighlighted = isHighlighted && !noGapBetweenElements; | ||
const isTitleString = typeof title === 'string'; | ||
const toggleOpen = () => { | ||
dispatch({ | ||
type: 'accordion/SET_OPENED', | ||
payload: {id, value: isHidden}, | ||
}); | ||
onItemSelect(id, isCollapsed); | ||
}; | ||
@@ -88,14 +88,2 @@ | ||
useEffect(() => { | ||
if (defaultOpened) { | ||
dispatch({ | ||
type: 'accordion/SET_OPENED', | ||
payload: {id, value: true}, | ||
}); | ||
} | ||
hasRendered.current = true; | ||
//eslint-disable-next-line | ||
}, []); | ||
useLayoutEffect(() => { | ||
const content = contentRef.current; | ||
@@ -166,6 +154,4 @@ | ||
if (hasRendered.current === false) return; | ||
isCollapsed ? collapse() : expand(); | ||
isHidden ? collapse() : expand(); | ||
return () => { | ||
@@ -177,3 +163,3 @@ if (!content) { | ||
}; | ||
}, [isHidden, reduceMotion]); | ||
}, [isCollapsed, reduceMotion]); | ||
@@ -204,3 +190,3 @@ return ( | ||
onBlur={handleBlur} | ||
aria-expanded={!isHidden} | ||
aria-expanded={!isCollapsed} | ||
aria-controls={contentId} | ||
@@ -216,10 +202,14 @@ id={id} | ||
> | ||
<Link | ||
size={titleSize} | ||
color="black" | ||
weight="bold" | ||
underlined={isHighlighted} | ||
> | ||
{title} | ||
</Link> | ||
{isTitleString ? ( | ||
<Link | ||
size={titleSize} | ||
color="black" | ||
weight="bold" | ||
underlined={isHighlighted} | ||
> | ||
{title} | ||
</Link> | ||
) : ( | ||
<span className="sg-accordion-item__title">{title}</span> | ||
)} | ||
<Flex | ||
@@ -236,3 +226,3 @@ justifyContent="center" | ||
className={cx('sg-accordion-item__arrow', { | ||
'sg-accordion-item__arrow--visible': !isHidden, | ||
'sg-accordion-item__arrow--visible': !isCollapsed, | ||
})} | ||
@@ -239,0 +229,0 @@ /> |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
8049702
1015
38090
12