Skip to content

Commit

Permalink
Add ability to select stories from inside of the StoryPanel
Browse files Browse the repository at this point in the history
  • Loading branch information
igor-dv committed Mar 6, 2018
1 parent 4465ffa commit f6965c0
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 23 deletions.
131 changes: 112 additions & 19 deletions addons/storysource/src/StoryPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -18,22 +23,45 @@ 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);

this.state = { source: '// Here will be dragons 🐉' };

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() {
Expand All @@ -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({
Expand All @@ -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 (
<div key={storyKey} ref={this.setSelectedStoryRef} style={styles.selectedStory}>
{story}
</div>
);
}

return (
<div
key={storyKey}
onClick={() => this.clickOnStory(kindStory)}
onKeyDown={e => this.keyDownOnStory(e, kindStory)}
tabIndex="0"
style={styles.story}
role="link"
>
{story}
</div>
);
}

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 (
<span>
{start}
<div ref={this.setSelectedStoryRef} style={styles.selections}>
{selected}
</div>
{end}
</span>
);
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 <span>{parts.map(part => part)}</span>;
}

render() {
Expand All @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions addons/storysource/src/manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: () => <StoryPanel channel={channel} />,
render: () => <StoryPanel channel={channel} api={api} />,
});
});
}
5 changes: 3 additions & 2 deletions addons/storysource/src/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
}

Expand Down

0 comments on commit f6965c0

Please sign in to comment.