diff --git a/src/index.js b/src/index.js index 3140084f9..2a82c9ceb 100644 --- a/src/index.js +++ b/src/index.js @@ -3,9 +3,10 @@ import React from 'react'; import ReactDOM from 'react-dom'; import isFinite from 'lodash/isFinite'; import { - getComponentNameFromHash, + getInfoFromHash, filterComponentExamples, filterComponentsInSectionsByExactName, + filterSections, processSections, setSlugs, slugger, @@ -27,24 +28,31 @@ function renderStyleguide() { // Parse URL hash to check if the components list must be filtered const { - // Name of the filtered component to show isolated (/#!/Button → Button) - targetComponentName, + // Name of the filtered component/section to show isolated (/#!/Button → Button) + targetName, // Index of the fenced block example of the filtered component isolate (/#!/Button/1 → 1) - targetComponentIndex, - } = getComponentNameFromHash(); + targetIndex, + } = getInfoFromHash(); let isolatedComponent = false; let isolatedExample = false; + let isolatedSection = false; // Filter the requested component id required - if (targetComponentName) { - const filteredComponents = filterComponentsInSectionsByExactName(sections, targetComponentName); - sections = [{ components: filteredComponents }]; - isolatedComponent = true; + if (targetName) { + const filteredComponents = filterComponentsInSectionsByExactName(sections, targetName); + if (filteredComponents.length) { + sections = [{ components: filteredComponents }]; + isolatedComponent = true; + } + else { + sections = [filterSections(sections, targetName)]; + isolatedSection = true; + } // If a single component is filtered and a fenced block index is specified hide the other examples - if (filteredComponents.length === 1 && isFinite(targetComponentIndex)) { - filteredComponents[0] = filterComponentExamples(filteredComponents[0], targetComponentIndex); + if (filteredComponents.length === 1 && isFinite(targetIndex)) { + filteredComponents[0] = filterComponentExamples(filteredComponents[0], targetIndex); isolatedExample = true; } } @@ -62,6 +70,7 @@ function renderStyleguide() { sections={sections} isolatedComponent={isolatedComponent} isolatedExample={isolatedExample} + isolatedSection={isolatedSection} />, document.getElementById('app') ); diff --git a/src/rsg-components/Section/Section.js b/src/rsg-components/Section/Section.js index f48aa1779..e11e2726f 100644 --- a/src/rsg-components/Section/Section.js +++ b/src/rsg-components/Section/Section.js @@ -5,7 +5,7 @@ import Components from 'rsg-components/Components'; import Sections from 'rsg-components/Sections'; import SectionRenderer from 'rsg-components/Section/SectionRenderer'; -export default function Section({ section }) { +export default function Section({ section }, { isolatedSection = false }) { const { name, slug, content, components, sections } = section; const contentJsx = content && ( @@ -21,6 +21,7 @@ export default function Section({ section }) { sections={sections} /> ); + return ( ); } @@ -35,3 +37,7 @@ export default function Section({ section }) { Section.propTypes = { section: PropTypes.object.isRequired, }; + +Section.contextTypes = { + isolatedSection: PropTypes.bool, +}; diff --git a/src/rsg-components/Section/SectionRenderer.js b/src/rsg-components/Section/SectionRenderer.js index e1e10d51e..05cd626fe 100644 --- a/src/rsg-components/Section/SectionRenderer.js +++ b/src/rsg-components/Section/SectionRenderer.js @@ -2,24 +2,52 @@ import React from 'react'; import PropTypes from 'prop-types'; import Styled from 'rsg-components/Styled'; import Heading from 'rsg-components/Heading'; +import Link from 'rsg-components/Link'; const styles = ({ space, fontFamily, fontSize }) => ({ root: { marginBottom: space[4], + '&:hover $isolatedLink': { + isolate: false, + opacity: 1, + }, }, heading: { margin: [[0, 0, space[2]]], fontFamily: fontFamily.base, fontSize: fontSize.h1, }, + isolatedLink: { + position: 'absolute', + top: 0, + right: 0, + fontFamily: fontFamily.base, + fontSize: fontSize.base, + opacity: 0, + transition: 'opacity ease-in-out .15s .2s', + }, + titleWrapper: { + position: 'relative', + }, }); -export function SectionRenderer({ classes, name, slug, content, components, sections }) { +export function SectionRenderer({ classes, name, slug, content, components, sections, isolatedSection }) { return (
- {name && ( - {name} - )} +
+ {name && ( + {name} + )} +
+ {name && ( + isolatedSection ? ( + ⇽ Back + ) : ( + Open isolated ⇢ + ) + )} +
+
{content} {components} {sections} @@ -34,6 +62,7 @@ SectionRenderer.propTypes = { content: PropTypes.node, components: PropTypes.node, sections: PropTypes.node, + isolatedSection: PropTypes.bool, }; export default Styled(styles)(SectionRenderer); diff --git a/src/rsg-components/Section/__snapshots__/Section.spec.js.snap b/src/rsg-components/Section/__snapshots__/Section.spec.js.snap index a73fc0246..eb7ab25c7 100644 --- a/src/rsg-components/Section/__snapshots__/Section.spec.js.snap +++ b/src/rsg-components/Section/__snapshots__/Section.spec.js.snap @@ -1,15 +1,30 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`render should not render title if name is not set 1`] = `
`; +exports[`render should not render title if name is not set 1`] = ` +
+
+
+
+
+`; exports[`render should render component 1`] = `
- <_class - level={1} - slug="foo" - > - Foo - +
+ <_class + level={1} + slug="foo" + > + Foo + +
+ <_class + href="#!/Foo" + > + Open isolated ⇢ + +
+
- <_class - level={1} - slug="test" - > - test - +
+ <_class + level={1} + slug="test" + > + test + +
+ <_class + href="#!/test" + > + Open isolated ⇢ + +
+
`; exports[`should not render components list if not defined 1`] = ` <_class + isolatedSection={false} name="No components" slug="no-components" /> @@ -54,6 +79,7 @@ exports[`should not render components list if not defined 1`] = ` exports[`should not render sections if not defined 1`] = ` <_class + isolatedSection={false} name="No sections" slug="no-sections" /> @@ -83,6 +109,7 @@ exports[`should render component renderer 1`] = ` } /> } + isolatedSection={false} name="Foo" sections={ } + isolatedSection={false} name="Components" slug="components" /> @@ -107,6 +135,7 @@ exports[`should render components list 1`] = ` exports[`should render sections if defined 1`] = ` <_class + isolatedSection={false} name="Nested sections" sections={ { }); }); -describe('getComponentNameFromHash', () => { +describe('getInfoFromHash', () => { it('should return important part of hash if it contains component name', () => { - const result = utils.getComponentNameFromHash('#!/Button'); - expect(result).toEqual({ targetComponentName: 'Button', targetComponentIndex: null }); + const result = utils.getInfoFromHash('#!/Button'); + expect(result).toEqual({ targetName: 'Button', targetIndex: null }); }); it('should return an empty object if hash contains no component name', () => { - const result = utils.getComponentNameFromHash('Button'); + const result = utils.getInfoFromHash('Button'); expect(result).toEqual({}); }); }); +describe('filterSections', () => { + const sections = [ + { + name: 'General', + sections: [], + }, + { + name: 'Forms', + sections: [], + }, + { + name: 'Lists', + sections: [], + }, + ]; + + it('should return the Forms section', () => { + const result = utils.filterSections(sections, 'Forms'); + expect(result).toEqual(sections[1]); + }); + + it('should return the Lists section', () => { + const result = utils.filterSections(sections, 'Lists'); + expect(result).toEqual(sections[2]); + }); +}); + describe('filterComponentExamples', () => { it('should return a shallow copy of the component with example filtered by given index', () => { const comp = { diff --git a/src/utils/utils.js b/src/utils/utils.js index 833348581..2181f9603 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -149,21 +149,31 @@ export function filterComponentsInSectionsByExactName(sections, name) { } /** - * Returns an object containing component name and, optionally, an example index + * Filters the sections to find the one with the matching name + * @param {Array} sections The styleguide sections + * @param {string} name The name to match + * @return {object} The section found + */ +export function filterSections(sections, name) { + return sections.find(section => section.name === name); +} + +/** + * Returns an object containing component/section name and, optionally, an example index * from hash part or page URL: - * http://localhost:6060/#!/Button → { targetComponentName: 'Button' } - * http://localhost:6060/#!/Button/1 → { targetComponentName: 'Button', targetComponentIndex: 1 } + * http://localhost:6060/#!/Button → { targetName: 'Button' } + * http://localhost:6060/#!/Button/1 → { targetName: 'Button', targetIndex: 1 } * * @param {string} [hash] * @returns {object} */ -export function getComponentNameFromHash(hash = window.location.hash) { +export function getInfoFromHash(hash = window.location.hash) { if (hash.substr(0, 3) === '#!/') { const tokens = hash.substr(3).split('/'); const index = parseInt(tokens[1], 10); return { - targetComponentName: tokens[0], - targetComponentIndex: isNaN(index) ? null : index, + targetName: tokens[0], + targetIndex: isNaN(index) ? null : index, }; } return {};