diff --git a/src/content/context.js b/src/content/context.js new file mode 100644 index 00000000..f1815684 --- /dev/null +++ b/src/content/context.js @@ -0,0 +1,11 @@ +import { createContext } from 'react' + +const defaultContext = { + level: 1, + parentId: null, +} + +const SectionContext = createContext(defaultContext) + +export default SectionContext +export { defaultContext } diff --git a/src/content/heading.jsx b/src/content/heading.jsx new file mode 100644 index 00000000..d6807c75 --- /dev/null +++ b/src/content/heading.jsx @@ -0,0 +1,38 @@ +import React, { forwardRef, useContext } from 'react' +import PropTypes from 'prop-types' + +import { Heading } from '../elements' +import Context from './context' + +const SectionHeading = forwardRef( + ({ children, id: forceId, level: forceLevel, ...passProps }, ref) => { + const parentContext = useContext(Context) + const level = forceLevel ?? parentContext.level ?? 1 + const id = forceId ?? parentContext.headingId + + return ( + + {children} + + ) + } +) + +SectionHeading.propTypes = { + /** + * A local level to force the current heading heading level over the one + * passed via the context from the parent sectioning element. + * + * If `null` or no value (`undefined`) is passed, the prop is ignored. + * Otherwise, it forces the current Heading to use the passed level. + * + * In contrast to the forced `level` in the Section, this one impacts + * only the current heading and will not have effect on nested sections. + * + * The prop is created to provide the full control over the component if + * the automatic system does not work. However, we recommend avoid using it. + */ + level: PropTypes.oneOf([undefined, null, 1, 2, 3, 4, 5, 6]), +} + +export default SectionHeading diff --git a/src/content/index.js b/src/content/index.js new file mode 100644 index 00000000..a5eb7468 --- /dev/null +++ b/src/content/index.js @@ -0,0 +1,2 @@ +export Section from './section' +export Heading from './heading' diff --git a/src/content/section.jsx b/src/content/section.jsx new file mode 100644 index 00000000..0d28ea37 --- /dev/null +++ b/src/content/section.jsx @@ -0,0 +1,51 @@ +import React, { forwardRef, useContext } from 'react' +import PropTypes from 'prop-types' + +import Context from './context' + +const Section = forwardRef( + ( + { children, id, level: forceLevel, tag: Tag = 'section', ...restProps }, + ref + ) => { + const parentContext = useContext(Context) + + const level = forceLevel ?? parentContext.level + 1 + const headingId = `${id}-title` + + const ariaProps = { + 'aria-labelledby': headingId, + } + + const currentContext = { + level, + id, + headingId, + } + + return ( + + {children} + + ) + } +) + +Section.propTypes = { + /** + * A section level to force the current section heading level over the one + * passed via the context. + * + * If `null` or no value (`undefined`) is passed, the prop is ignored. + * Otherwise, it forces descendent Heading to use the passed level. + * + * Be aware, forcing the section level impacts not only the current section + * heading but all descendent sections too. + * + * The prop is created to provide the full control over the component if + * the automatic system does not work. However, we recommend avoid using it. + */ + level: PropTypes.oneOf([undefined, null, 1, 2, 3, 4, 5, 6]), +} + +export default Section