|
| 1 | +# MapComponents MapLibre |
| 2 | + |
| 3 | +## Getting started |
| 4 | + |
| 5 | +1. Clone the repository |
| 6 | +2. ```cd``` into the folder and |
| 7 | +3. Run ```yarn``` to install all dependencies. |
| 8 | +4. Run ```yarn storybook``` to start the storybook server. It will watch files for changes and hot-reload affected components. If cleanup functions are incomplete it can be required to reload the browser. |
| 9 | + |
| 10 | +### Create a new component |
| 11 | + |
| 12 | +2. Run ```yarn create-component {component-name}``` to create a new Map-component based on ./src/components/MlComponentTemplate/. It must start with a capital letter because it is a react component and preferably start with the prefix "Ml" if it is a MapLibre component, to follow the naming conventions of this repository. |
| 13 | +2. The new component should become available within your storybook webinterface. Start the component development inside the component file (former MlComponentTemplate.js) and see the changes reflected in your browser. |
| 14 | +3. Once the component is ready to be published to the MapComponents catalogue, remove the ```_``` from the meta.json file ({component_name}.meta_.json) and it will be included in the next release. |
| 15 | + |
| 16 | +### Create a new example application |
| 17 | + |
| 18 | +1. Follow all steps of "Create a new component" |
| 19 | +2. Change the value of the property "type" in {component_name}.meta.json to "application" |
| 20 | + |
| 21 | +## Anatomy of a MapComponent |
| 22 | + |
| 23 | +A MapComponent is a react component that accepts at least 1 attribute "mapId" and is expected to retrieve and directly manipulate a maplibre-gl instance from mapContext. |
| 24 | +An example implementation of basic required functions for the maplibre instance retrieval process using the functions getMap, mapExists provided by mapContext, both accepting "mapId" (string) as parameter, can be seen in ./components/MlBasicCompontent.js. For components with a basic functionality it may be sufficient to make use of the MlBasicComponent and just provide the attributes "mapId" (string), "mapIsReady" (function), "cleanup" (function) as can be seen in ./components/MlDeckGlLayer/MlDeckGlLayer. |
| 25 | +If no attribute mapId is provided the map component is expected to work with the map instance provided by mapContext at ```mapContext.map```. |
| 26 | + |
| 27 | + |
| 28 | +### File structure |
| 29 | + |
| 30 | +``` |
| 31 | +./src/components/{component_name}/ |
| 32 | +├── {component_name}.doc.de.md |
| 33 | +├── {component_name}.meta.json |
| 34 | +├── {component_name}.js |
| 35 | +├── {component_name}.test.js |
| 36 | +└── {component_name}.stories.js |
| 37 | +``` |
| 38 | + |
| 39 | +### {component_name}.js |
| 40 | + |
| 41 | +React component implementation |
| 42 | + |
| 43 | +#### Common conventions |
| 44 | + |
| 45 | +##### Cleanup functions |
| 46 | + |
| 47 | +To make sure a component cleans up the MapLibre instance after it has been removed from reactDOM declare a reference to the map instance using the useRef hook. |
| 48 | + |
| 49 | +**- Reference declaration** |
| 50 | + |
| 51 | +``` |
| 52 | + const mapRef = useRef(null); |
| 53 | +``` |
| 54 | + |
| 55 | +**- Component cleanup function** |
| 56 | + |
| 57 | +After everything has been undone it is important to set the map reference (mapRef.current) to null. |
| 58 | + |
| 59 | +``` |
| 60 | + useEffect(() => { |
| 61 | + return () => { |
| 62 | + // This is the cleanup function, it is called when this react component is removed from reactDOM |
| 63 | + if (mapRef.current) { |
| 64 | + if (mapRef.current.style && mapRef.current.getLayer(layerId)) { |
| 65 | + mapRef.current.removeLayer(layerId); |
| 66 | + } |
| 67 | + if (mapRef.current.style && mapRef.current.getSource(layerSourceId)) { |
| 68 | + mapRef.current.removeSource(layerSourceId); |
| 69 | + } |
| 70 | +
|
| 71 | + mapRef.current = null; |
| 72 | + } |
| 73 | + }; |
| 74 | + }, []); |
| 75 | +``` |
| 76 | + |
| 77 | +**- Reference population** |
| 78 | + |
| 79 | +This happens within the effect where the targeted (through props.mapId) map instance is discovered for the first time. |
| 80 | + |
| 81 | +``` |
| 82 | + mapRef.current = mapContext.getMap(props.mapId); |
| 83 | +``` |
| 84 | + |
| 85 | +### {component_name}.meta.json |
| 86 | + |
| 87 | +Additional meta data regarding the component, this file is required for the component to become listed in the catalogue |
| 88 | + |
| 89 | +``` |
| 90 | +{ |
| 91 | + "name": "{component_name}", // must be identical to the react component name (string) |
| 92 | + "title": "", // german component title (string) |
| 93 | + "description": "", // german short description (string) |
| 94 | + "tags": [ "Map add-on" ], // list of tags (Array<string>) |
| 95 | + "category": "add-ons", // category (string) |
| 96 | + "type": "component", // type "component" or "application" (string) |
| 97 | +} |
| 98 | +``` |
| 99 | + |
| 100 | +### {component_name}.doc.de.md |
| 101 | + |
| 102 | +Description text, that is shown on the catalogue component detail page below the main image |
| 103 | + |
| 104 | +### {component_name}.stories.js |
| 105 | + |
| 106 | +Example implementation of a component in context with all required dependent components to showcase the basic functionality of a single component. Decorators to choose from are located in ./src/decorators/. During development the command ```yarn storybook``` will start a server (localhost:6006) with live reload functionality. In case of example applications the stories are used as a wrapper to make the application available in the storybook build that is later used to access working demos from within the catalogue. |
| 107 | + |
| 108 | +Storybook stories are also used to generate screenshots of each component. The command ```yarn test``` will run automated jest tests. To make a component screenshot appear in the catalogue manually create a png like ./public/thumbnails/{component_name}.png and push it to the repository, it will be included in the catalogue in the next catalogue deployment. |
| 109 | + |
| 110 | +More information on writing storybook stories for react components: https://storybook.js.org/docs/react/get-started/browse-stories |
| 111 | + |
| 112 | +## LoadingOverlay and LoadingOverlayProvider (currently located in ./ui_components/) |
| 113 | + |
| 114 | +The loading overlay component is added in the storybook decorator. |
| 115 | +Without any further configuration it will listen for new MapLibre instances registered in MapContext and fade out once all of them have fired an "IDLE" event. For more precise control the LoadingOverlayContext provides a ```loadingOverlayContext.setControlled(true)``` function that will, if called with true as first parameter, switch the LoadingOverlay to manual control. Once the application has loaded completely call the ```loadingOverlayContext.setLoadingDone(true)``` function to trigger the LoadingOverlay Component to fade out. |
| 116 | + |
| 117 | +For decorator integration examples check the storybook decorators located in ./decorators/. |
| 118 | +For controlled LoadingOverlay examples please see MlLaermkarte (story & component). |
0 commit comments