Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate components to React hooks and use new context API #1018

Merged
merged 22 commits into from
Nov 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d66e6ae
Hook-ify Accordion
jumpinjackie Nov 21, 2019
df97775
Update Accordion tests in light of hook-ification. A function compone…
jumpinjackie Nov 21, 2019
270aa65
Hook-ify BaseLayerSwitcher and remove safe-prop module and usages of …
jumpinjackie Nov 21, 2019
20ffb25
Fix incorrect name
jumpinjackie Nov 21, 2019
2190ca1
Momentarily revert BaseLayerSwticher back to class component as some …
jumpinjackie Nov 21, 2019
bf4df85
Hook-ify FlyoutRegion
jumpinjackie Nov 21, 2019
c202a54
Hook-ify Legend and Toolbar components. Since we're hook-ifying the L…
jumpinjackie Nov 21, 2019
cbe9605
Fix up legend unit tests due to switch to new context API.
jumpinjackie Nov 21, 2019
da2a9f9
Complete hook-ification of the Legend and Toolbar components
jumpinjackie Nov 21, 2019
3087123
Tidy up commentary
jumpinjackie Nov 21, 2019
1e1148a
Hook-ify MapMenu, MenuComponent and Navigator. Tidy up imports for mo…
jumpinjackie Nov 21, 2019
7d7435b
Hook-ify ScaleDisplay and SelectionPanel components. Fix up Selection…
jumpinjackie Nov 21, 2019
5bc1dac
#1015: Remove use of lodash.uniq (just implement our own makeUnique()…
jumpinjackie Nov 21, 2019
d3b2037
#1015: Remove the use of lodash.xor as we're only using to test for a…
jumpinjackie Nov 21, 2019
922d98e
Version bump
jumpinjackie Nov 21, 2019
208dad3
Package updates
jumpinjackie Nov 22, 2019
03005e7
Plug tooltip hyperlink localization hole.
jumpinjackie Nov 22, 2019
894eb22
Use command label as window title if invocation target is a modal dia…
jumpinjackie Nov 22, 2019
c4e7416
Merge remote-tracking branch 'origin/0.12.x' into feature-hookification
jumpinjackie Nov 22, 2019
3916b39
Some final hook-ification and note compnents that can't be converted …
jumpinjackie Nov 23, 2019
10da14b
Fix up BaseLayerSwitcher styling.
jumpinjackie Nov 23, 2019
bd2a58b
Package updates
jumpinjackie Nov 23, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 0.12.7.{build}
version: 0.13.0.{build}
shallow_clone: true
# Don't actually build. This is not a .net project
build: off
Expand Down
2 changes: 1 addition & 1 deletion docs_dev/content/API_NPM.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

`mapguide-react-layout` is a modern map viewer for [MapGuide Open Source](http://mapguide.osgeo.org) and [Autodesk Infrastructure Map Server](http://www.autodesk.com/products/infrastructure-map-server/overview)

This API documentation covers the `mapguide-react-layout` npm module (version **0.12.0**)
This API documentation covers the `mapguide-react-layout` npm module (version **0.13.0**)

For an example of how this npm module is used, check out the [mapguide-react-layout-example](https://github.com/jumpinjackie/mapguide-react-layout-example)

Expand Down
18 changes: 7 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mapguide-react-layout",
"version": "0.12.7",
"version": "0.13.0",
"description": "A modern react-based map viewer for MapGuide",
"scripts": {
"prebuild": "yarn run clean",
Expand Down Expand Up @@ -44,12 +44,10 @@
},
"homepage": "https://github.com/jumpinjackie/mapguide-react-layout#readme",
"dependencies": {
"@blueprintjs/core": "3.19.1",
"@blueprintjs/core": "^3.21.0",
"history": "^4.10.1",
"ismobilejs": "^1.0.3",
"lodash.debounce": "^4.0.8",
"lodash.uniq": "^4.5.0",
"lodash.xor": "^4.5.0",
"ol": "4.6.5",
"proj4": "2.6.0",
"prop-types": "^15.7.2",
Expand Down Expand Up @@ -77,11 +75,9 @@
"@types/enzyme": "3.10.3",
"@types/jest": "24.0.23",
"@types/lodash.debounce": "4.0.6",
"@types/lodash.uniq": "4.5.6",
"@types/lodash.xor": "4.5.6",
"@types/prop-types": "15.7.3",
"@types/qs": "^6.9.0",
"@types/react": "16.9.11",
"@types/react": "16.9.12",
"@types/react-addons-css-transition-group": "15.0.5",
"@types/react-addons-update": "0.14.20",
"@types/react-copy-to-clipboard": "^4.3.0",
Expand All @@ -99,7 +95,7 @@
"css-loader": "3.2.0",
"enzyme": "3.10.0",
"enzyme-adapter-react-16": "1.15.1",
"file-loader": "4.2.0",
"file-loader": "4.3.0",
"fork-ts-checker-webpack-plugin": "^3.1.0",
"jest": "24.9.0",
"jsdom": "^15.2.1",
Expand All @@ -115,14 +111,14 @@
"rimraf": "3.0.0",
"source-map-loader": "0.2.4",
"style-loader": "1.0.0",
"testcafe": "^1.6.1",
"testcafe": "^1.7.0",
"testcafe-react-selectors": "^3.3.0",
"thread-loader": "^2.1.3",
"ts-jest": "24.1.0",
"ts-jest": "24.2.0",
"ts-loader": "6.2.1",
"tslint": "5.20.1",
"typescript": "3.7.2",
"url-loader": "2.2.0",
"url-loader": "2.3.0",
"webpack": "4.41.2",
"webpack-cli": "^3.3.10"
},
Expand Down
14 changes: 8 additions & 6 deletions src/actions/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import * as shortid from "shortid";
import { registerStringBundle, DEFAULT_LOCALE } from "../api/i18n";
import { assertNever } from "../utils/never";
import proj4 from "proj4";
import uniq = require("lodash.uniq");
import { makeUnique } from "../utils/array";
import { ensureParameters } from "../utils/url";
import { strIsNullOrEmpty } from "../utils/string";
import { convertWidget, isFlyoutSpec, ToolbarConf, PreparedSubMenuSet } from '../api/registry/command-spec';
Expand Down Expand Up @@ -436,7 +436,7 @@ function getExtraProjectionsFromFlexLayout(appDef: ApplicationDefinition): strin
}
}
}
return uniq(epsgs);
return makeUnique(epsgs);
}

function processAndDispatchInitError(error: Error, includeStack: boolean, dispatch: ReduxDispatch, opts: IInitAsyncOptions): void {
Expand Down Expand Up @@ -599,7 +599,8 @@ async function initFromWebLayoutAsync(webLayout: WebLayout, opts: IInitAsyncOpti
targetFrame: cmd.TargetFrame,
parameters: (cmd.AdditionalParameter || []).map(p => {
return { name: p.Key, value: p.Value };
})
}),
title: cmd.Label
});
} else if (isSearchCommand(cmd)) {
registerCommand(cmd.Name, {
Expand All @@ -610,7 +611,7 @@ async function initFromWebLayoutAsync(webLayout: WebLayout, opts: IInitAsyncOpti
resultColumns: cmd.ResultColumns,
filter: cmd.Filter,
matchLimit: cmd.MatchLimit,
title: cmd.Label,
title: cmd.Label
});
}
cmdsByKey[cmd.Name] = cmd;
Expand Down Expand Up @@ -748,7 +749,7 @@ async function initFromAppDefAsync(appDef: ApplicationDefinition, opts: IInitAsy
resultColumns: cmd.ResultColumns,
filter: cmd.Filter,
matchLimit: cmd.MatchLimit,
title: cmd.Title,
title: (cmd.Title || (isUIWidget(widget) ? widget.Label : undefined)),
target: convertToCommandTarget(cmd.Target),
targetFrame: cmd.Target
});
Expand All @@ -761,7 +762,8 @@ async function initFromAppDefAsync(appDef: ApplicationDefinition, opts: IInitAsy
targetFrame: cmd.Target,
parameters: (cmd.AdditionalParameter || []).map((p: any) => {
return { name: p.Key, value: p.Value };
})
}),
title: isUIWidget(widget) ? widget.Label : undefined
});
break;
}
Expand Down
4 changes: 2 additions & 2 deletions src/actions/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { Client } from "../api/client";
import { QueryMapFeaturesResponse, FeatureSet, SelectedFeature, SelectedFeatureSet } from '../api/contracts/query';
import { IQueryMapFeaturesOptions } from '../api/request-builder';
import { buildSelectionXml } from '../api/builders/deArrayify';
import uniq = require("lodash.uniq");
import { makeUnique } from "../utils/array";
import { ActionType } from '../constants/actions';
import { IMapSetBusyCountAction, IMapSetBaseLayerAction, IMapSetScaleAction, IMapSetMouseCoordinatesAction, IMapSetLayerTransparencyAction, IMapSetViewSizeUnitsAction, IMapPreviousViewAction, IMapNextViewAction, ISetActiveMapToolAction, ISetActiveMapAction, ISetManualFeatureTooltipsEnabledAction, ISetFeatureTooltipsEnabledAction, IMapSetViewRotationAction, IMapSetViewRotationEnabledAction, IShowSelectedFeatureAction, IMapSetSelectionAction, IMapResizedAction } from './defs';
import { storeSelectionSet } from '../api/session-store';
Expand Down Expand Up @@ -90,7 +90,7 @@ function combineFeatureSets(oldRes: FeatureSet | undefined, newRes: FeatureSet |
if (existing.length == 0) {
merged.Layer.push(layer);
} else {
existing[0].Class.ID = uniq(existing[0].Class.ID.concat(layer.Class.ID));
existing[0].Class.ID = makeUnique(existing[0].Class.ID.concat(layer.Class.ID));
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion src/api/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ export interface ICommand {
icon?: string;
iconClass?: string;
//tooltip?: string;
//label?: string;
/**
* @since 0.12.8
*/
title?: string;
/**
* Indicates if this command is enabled based on the given application state
*
Expand Down Expand Up @@ -242,6 +245,10 @@ export interface IInvokeUrlCommand extends ITargetedCommand {
*/
icon?: string;
iconClass?: string;
/**
* @since 0.12.8
*/
title?: string;
/**
* The URL to invoke
*
Expand Down
6 changes: 4 additions & 2 deletions src/api/registry/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ export function registerCommand(name: string, cmdDef: ICommand | IInvokeUrlComma
cmd = {
icon: cmdDef.icon,
iconClass: cmdDef.iconClass,
title: cmdDef.title,
enabled: (state) => {
if (cmdDef.disableIfSelectionEmpty === true) {
return CommandConditions.hasSelection(state);
Expand All @@ -362,14 +363,15 @@ export function registerCommand(name: string, cmdDef: ICommand | IInvokeUrlComma
if (map) {
const params = mergeInvokeUrlParameters(cmdDef.parameters, parameters);
const url = ensureParameters(cmdDef.url, map.Name, map.SessionId, config.locale, true, params);
openUrlInTarget(name, cmdDef, config.capabilities.hasTaskPane, dispatch, url);
openUrlInTarget(name, cmdDef, config.capabilities.hasTaskPane, dispatch, url, cmd.title);
}
}
};
} else if (isSearchCommand(cmdDef)) {
cmd = {
icon: cmdDef.icon,
iconClass: cmdDef.iconClass,
title: cmdDef.title,
enabled: () => true,
selected: () => false,
invoke: (dispatch: ReduxDispatch, getState: () => IApplicationState, viewer: IMapViewer, parameters?: any) => {
Expand All @@ -388,7 +390,7 @@ export function registerCommand(name: string, cmdDef: ICommand | IInvokeUrlComma
+ `&limit=${cmdDef.matchLimit}`
+ `&properties=${(cmdDef.resultColumns.Column || []).map(col => col.Property).join(",")}`
+ `&propNames=${(cmdDef.resultColumns.Column || []).map(col => col.Name).join(",")}`;
openUrlInTarget(name, cmdDef, config.capabilities.hasTaskPane, dispatch, url);
openUrlInTarget(name, cmdDef, config.capabilities.hasTaskPane, dispatch, url, cmd.title);
}
}
};
Expand Down
106 changes: 42 additions & 64 deletions src/components/accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import * as React from "react";
import {
GenericEvent
} from "../api/common";
import { safePropAccess } from '../utils/safe-prop';
import Icon from 'ol/style/icon';
import { Collapse, Icon as BpIcon, ResizeSensor, IResizeEntry } from '@blueprintjs/core';

/**
Expand Down Expand Up @@ -45,72 +43,52 @@ export interface IAccordionProps {

const PANEL_HEADER_HEIGHT = 24;

function validatePanelId(panels: IAccordionPanelSpec[], id: string | undefined): string | null {
if (!id) {
return null;
}
const panel = panels.filter(p => p.id == id)[0];
if (panel) {
return id;
}
return null;
}

/**
* A generic, reusable Accordion component
*
* @export
* @class Accordion
* @extends {React.Component<IAccordionProps, any>}
* @param props
*/
export class Accordion extends React.Component<IAccordionProps, any> {
constructor(props: IAccordionProps) {
super(props);
const activeId = this.validatePanelId(props.panels, props.activePanelId);
this.state = {
openPanel: activeId || props.panels[props.panels.length - 1].id,
dim: {
width: -1,
height: -1
}
};
}
private onTogglePanel = (e: GenericEvent) => {
export const Accordion = (props: IAccordionProps) => {
const { style, panels, isResizing, onActivePanelChanged } = props;
const activeId = validatePanelId(props.panels, props.activePanelId);
const [dim, setDim] = React.useState<Pick<DOMRectReadOnly, "width" | "height">>({
width: -1,
height: -1
});
const [openPanel, setOpenPanel] = React.useState(activeId || panels[panels.length - 1].id);
const onResize = (entries: IResizeEntry[]) => {
setDim(entries[0].contentRect);
};
const onTogglePanel = (e: GenericEvent) => {
const id = e.currentTarget.attributes["data-accordion-panel-id"].value;
if (this.state.openPanel != id) {
this.setState({ openPanel: id }, () => {
safePropAccess(this.props, "onActivePanelChanged", func => func!(id));
});
if (openPanel != id) {
setOpenPanel(id);
onActivePanelChanged?.(id);
}
}
private validatePanelId(panels: IAccordionPanelSpec[], id: string | undefined): string | null {
if (!id) {
return null;
}
const panel = panels.filter(p => p.id == id)[0];
if (panel) {
return id;
}
return null;
}
private onResize = (entries: IResizeEntry[]) => {
this.setState({ dim: entries[0].contentRect });
}
componentDidUpdate(prevProps: IAccordionProps) {
const nextProps = this.props;
if (prevProps.activePanelId != nextProps.activePanelId) {
const newId = this.validatePanelId(nextProps.panels, nextProps.activePanelId);
if (newId) {
this.setState({ openPanel: newId });
}
}
}
render(): JSX.Element {
const { openPanel, dim } = this.state;
const { panels, style, isResizing } = this.props;
return <ResizeSensor onResize={this.onResize}>
<div style={style} className="component-accordion">
{panels.map(p => {
const isOpen = (p.id == openPanel);
return <div key={p.id} className="component-accordion-panel">
<div className="component-accordion-panel-header" style={{ height: PANEL_HEADER_HEIGHT }} data-accordion-panel-id={p.id} onClick={this.onTogglePanel}>
<BpIcon icon={isOpen ? "chevron-up" : "chevron-down"} /> {p.title}
</div>
<Collapse isOpen={isOpen}>
{p.contentRenderer({ width: dim.width, height: (dim.height - (panels.length * PANEL_HEADER_HEIGHT)) }, isResizing)}
</Collapse>
</div>;
})}
</div>
</ResizeSensor>;
}
return <ResizeSensor onResize={onResize}>
<div style={style} className="component-accordion">
{panels.map(p => {
const isOpen = (p.id == openPanel);
return <div key={p.id} className="component-accordion-panel">
<div className="component-accordion-panel-header" style={{ height: PANEL_HEADER_HEIGHT }} data-accordion-panel-id={p.id} onClick={onTogglePanel}>
<BpIcon icon={isOpen ? "chevron-up" : "chevron-down"} /> {p.title}
</div>
<Collapse isOpen={isOpen}>
{p.contentRenderer({ width: dim.width, height: (dim.height - (panels.length * PANEL_HEADER_HEIGHT)) }, isResizing)}
</Collapse>
</div>;
})}
</div>
</ResizeSensor>;
}
Loading