From f6965c0f1e85548a99833aefecfa2e8360339744 Mon Sep 17 00:00:00 2001 From: igor-dv Date: Tue, 6 Mar 2018 10:42:56 +0200 Subject: [PATCH] Add ability to select stories from inside of the StoryPanel --- addons/storysource/src/StoryPanel.js | 131 +++++++++++++++++++++++---- addons/storysource/src/manager.js | 4 +- addons/storysource/src/preview.js | 5 +- 3 files changed, 117 insertions(+), 23 deletions(-) diff --git a/addons/storysource/src/StoryPanel.js b/addons/storysource/src/StoryPanel.js index 52a195471704..cd6a5aa77f78 100644 --- a/addons/storysource/src/StoryPanel.js +++ b/addons/storysource/src/StoryPanel.js @@ -8,8 +8,13 @@ import { EVENT_ID } from './'; registerLanguage('jsx', jsx); +const keyCodeEnter = 13; + const styles = { - selections: { + story: { + cursor: 'pointer', + }, + selectedStory: { backgroundColor: 'rgba(255, 242, 60, 0.2)', }, panel: { @@ -18,6 +23,23 @@ const styles = { }; export default class StoryPanel extends Component { + static areLocationsEqual(a, b) { + return ( + a.startLoc.line === b.startLoc.line && + a.startLoc.col === b.startLoc.col && + a.endLoc.line === b.endLoc.line && + a.endLoc.col === b.endLoc.col + ); + } + + static getLocationKeys(locationsMap) { + return locationsMap + ? Array.from(Object.keys(locationsMap)).sort( + (key1, key2) => locationsMap[key1].startLoc.line - locationsMap[key2].startLoc.line + ) + : []; + } + constructor(props) { super(props); @@ -25,15 +47,21 @@ export default class StoryPanel extends Component { const { channel } = props; - channel.on(EVENT_ID, ({ source, location }) => { + channel.on(EVENT_ID, ({ source, currentLocation, locationsMap }) => { + const locationsKeys = StoryPanel.getLocationKeys(locationsMap); + this.setState({ source, - location, + currentLocation, + locationsMap, + locationsKeys, }); }); this.setSelectedStoryRef = this.setSelectedStoryRef.bind(this); this.lineRenderer = this.lineRenderer.bind(this); + this.clickOnStory = this.clickOnStory.bind(this); + this.keyDownOnStory = this.keyDownOnStory.bind(this); } componentDidUpdate() { @@ -46,6 +74,23 @@ export default class StoryPanel extends Component { this.selectedStoryRef = ref; } + clickOnStory(key) { + const { api } = this.props; + const [kind, story] = key.split('@'); + + if (kind && story) { + api.selectStory(kind, story); + } + } + + keyDownOnStory(e, key) { + if (e.keyCode !== keyCodeEnter) { + return; + } + + this.clickOnStory(key); + } + createPart(rows, stylesheet, useInlineStyles) { return rows.map((node, i) => createElement({ @@ -57,29 +102,74 @@ export default class StoryPanel extends Component { ); } - lineRenderer({ rows, stylesheet, useInlineStyles }) { - const { location } = this.state; + createStoryPart(rows, stylesheet, useInlineStyles, location, kindStory) { + const { currentLocation } = this.state; + const first = location.startLoc.line - 1; + const last = location.endLoc.line; + + const storyRows = rows.slice(first, last); + const story = this.createPart(storyRows, stylesheet, useInlineStyles); + const storyKey = `${first}-${last}`; - if (location) { + if (StoryPanel.areLocationsEqual(location, currentLocation)) { + return ( +
+ {story} +
+ ); + } + + return ( +
this.clickOnStory(kindStory)} + onKeyDown={e => this.keyDownOnStory(e, kindStory)} + tabIndex="0" + style={styles.story} + role="link" + > + {story} +
+ ); + } + + createParts(rows, stylesheet, useInlineStyles) { + const { locationsMap, locationsKeys } = this.state; + + const parts = []; + let lastRow = 0; + + locationsKeys.forEach(key => { + const location = locationsMap[key]; const first = location.startLoc.line - 1; const last = location.endLoc.line; - const start = this.createPart(rows.slice(0, first), stylesheet, useInlineStyles); - const selected = this.createPart(rows.slice(first, last), stylesheet, useInlineStyles); - const end = this.createPart(rows.slice(last), stylesheet, useInlineStyles); + const start = this.createPart(rows.slice(lastRow, first), stylesheet, useInlineStyles); + const storyPart = this.createStoryPart(rows, stylesheet, useInlineStyles, location, key); - return ( - - {start} -
- {selected} -
- {end} -
- ); + parts.push(start); + parts.push(storyPart); + + lastRow = last; + }); + + const lastPart = this.createPart(rows.slice(lastRow), stylesheet, useInlineStyles); + + parts.push(lastPart); + + return parts; + } + + lineRenderer({ rows, stylesheet, useInlineStyles }) { + const { locationsMap, locationsKeys } = this.state; + + if (!locationsMap || !locationsKeys.length) { + return this.createPart(rows, stylesheet, useInlineStyles); } - return this.createPart(rows, stylesheet, useInlineStyles); + const parts = this.createParts(rows, stylesheet, useInlineStyles); + + return {parts.map(part => part)}; } render() { @@ -98,6 +188,9 @@ export default class StoryPanel extends Component { } StoryPanel.propTypes = { + api: PropTypes.shape({ + selectStory: PropTypes.func.isRequired, + }).isRequired, channel: PropTypes.shape({ emit: PropTypes.func, on: PropTypes.func, diff --git a/addons/storysource/src/manager.js b/addons/storysource/src/manager.js index bdfd4b4d5db4..1523fefcea22 100644 --- a/addons/storysource/src/manager.js +++ b/addons/storysource/src/manager.js @@ -4,11 +4,11 @@ import StoryPanel from './StoryPanel'; import { ADDON_ID, PANEL_ID } from './'; export function register() { - addons.register(ADDON_ID, () => { + addons.register(ADDON_ID, api => { const channel = addons.getChannel(); addons.addPanel(PANEL_ID, { title: 'Story', - render: () => , + render: () => , }); }); } diff --git a/addons/storysource/src/preview.js b/addons/storysource/src/preview.js index 96c38ccd3358..393899a97611 100644 --- a/addons/storysource/src/preview.js +++ b/addons/storysource/src/preview.js @@ -7,11 +7,12 @@ function getLocation(context, locationsMap) { function setStorySource(context, source, locationsMap) { const channel = addons.getChannel(); - const location = getLocation(context, locationsMap); + const currentLocation = getLocation(context, locationsMap); channel.emit(EVENT_ID, { source, - location, + currentLocation, + locationsMap, }); }