mirror of
https://github.com/DOI-DO/j40-cejst-2.git
synced 2025-02-23 10:04:18 -08:00
Parameterize zoom experiments (#339)
* Adding ability to set flags in url * parameterizing tile layers
This commit is contained in:
parent
6c8d71c5b9
commit
3cd6e06115
6 changed files with 188 additions and 163 deletions
|
@ -25,7 +25,7 @@ const J40Header = () => {
|
||||||
const toggleMobileNav = (): void =>
|
const toggleMobileNav = (): void =>
|
||||||
setMobileNavOpen((prevOpen) => !prevOpen);
|
setMobileNavOpen((prevOpen) => !prevOpen);
|
||||||
|
|
||||||
const headerLinks = (flags: string[] | undefined) => {
|
const headerLinks = (flags: {[key: string] : any} | undefined) => {
|
||||||
// static map of all possible menu items. Originally, it was all strings,
|
// static map of all possible menu items. Originally, it was all strings,
|
||||||
// but we need to handle both onsite and offsite links.
|
// but we need to handle both onsite and offsite links.
|
||||||
const menuData = new Map<string, JSX.Element>([
|
const menuData = new Map<string, JSX.Element>([
|
||||||
|
@ -64,7 +64,7 @@ const J40Header = () => {
|
||||||
// select which items from the above map to show, right now it's only two
|
// select which items from the above map to show, right now it's only two
|
||||||
// possibilities so it's simple. Note: strings are used as react keys
|
// possibilities so it's simple. Note: strings are used as react keys
|
||||||
const menu =
|
const menu =
|
||||||
flags?.includes('sprint3') ?
|
('sprint3' in flags!) ?
|
||||||
['about', 'cejst', 'methodology', 'contact'] :
|
['about', 'cejst', 'methodology', 'contact'] :
|
||||||
['about', 'cejst', 'methodology', 'contact'];
|
['about', 'cejst', 'methodology', 'contact'];
|
||||||
// TODO: make feature flags flags work.
|
// TODO: make feature flags flags work.
|
||||||
|
|
|
@ -7,10 +7,12 @@ import maplibregl, {LngLatBoundsLike,
|
||||||
Popup,
|
Popup,
|
||||||
LngLatLike,
|
LngLatLike,
|
||||||
MapboxGeoJSONFeature} from 'maplibre-gl';
|
MapboxGeoJSONFeature} from 'maplibre-gl';
|
||||||
import mapStyle from '../data/mapStyle';
|
import {makeMapStyle} from '../data/mapStyle';
|
||||||
import PopupContent from './popupContent';
|
import PopupContent from './popupContent';
|
||||||
import * as constants from '../data/constants';
|
import * as constants from '../data/constants';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
import {useFlags} from '../contexts/FlagContext';
|
||||||
|
|
||||||
import 'maplibre-gl/dist/maplibre-gl.css';
|
import 'maplibre-gl/dist/maplibre-gl.css';
|
||||||
import * as styles from './J40Map.module.scss';
|
import * as styles from './J40Map.module.scss';
|
||||||
|
|
||||||
|
@ -27,11 +29,12 @@ const J40Map = () => {
|
||||||
const mapRef = useRef<Map>() as React.MutableRefObject<Map>;
|
const mapRef = useRef<Map>() as React.MutableRefObject<Map>;
|
||||||
const selectedFeature = useRef<MapboxGeoJSONFeature>();
|
const selectedFeature = useRef<MapboxGeoJSONFeature>();
|
||||||
const [zoom, setZoom] = useState(constants.GLOBAL_MIN_ZOOM);
|
const [zoom, setZoom] = useState(constants.GLOBAL_MIN_ZOOM);
|
||||||
|
const flags = useFlags();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initialMap = new Map({
|
const initialMap = new Map({
|
||||||
container: mapContainer.current!,
|
container: mapContainer.current!,
|
||||||
style: mapStyle,
|
style: makeMapStyle(flags),
|
||||||
center: constants.DEFAULT_CENTER as LngLatLike,
|
center: constants.DEFAULT_CENTER as LngLatLike,
|
||||||
zoom: zoom,
|
zoom: zoom,
|
||||||
minZoom: constants.GLOBAL_MIN_ZOOM,
|
minZoom: constants.GLOBAL_MIN_ZOOM,
|
||||||
|
|
|
@ -6,7 +6,7 @@ describe('URL params are parsed and passed to children', () => {
|
||||||
describe('when the URL has a "flags" parameter set', () => {
|
describe('when the URL has a "flags" parameter set', () => {
|
||||||
// We artificially set the URL to localhost?flags=1,2,3
|
// We artificially set the URL to localhost?flags=1,2,3
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
window.history.pushState({}, 'Test Title', '/?flags=1,2,3');
|
window.history.pushState({}, 'Test Title', '/?flags=1,2,3,test=4');
|
||||||
});
|
});
|
||||||
describe('when using useFlags', () => {
|
describe('when using useFlags', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -14,10 +14,11 @@ describe('URL params are parsed and passed to children', () => {
|
||||||
const flags = useFlags();
|
const flags = useFlags();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>{flags.includes('1') ? 'yes1' : 'no1'}</div>
|
<div>{'1' in flags ? 'yes1' : 'no1'}</div>
|
||||||
<div>{flags.includes('2') ? 'yes2' : 'no2'}</div>
|
<div>{'2' in flags ? 'yes2' : 'no2'}</div>
|
||||||
<div>{flags.includes('3') ? 'yes3' : 'no3'}</div>
|
<div>{'3' in flags ? 'yes3' : 'no3'}</div>
|
||||||
<div>{flags.includes('4') ? 'yes4' : 'no4'}</div>
|
<div>{'4' in flags ? 'yes4' : 'no4'}</div>
|
||||||
|
<div>{flags['test'] == 4 ? 'yes5' : 'no5'}</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -33,6 +34,7 @@ describe('URL params are parsed and passed to children', () => {
|
||||||
expect(screen.queryByText('yes2')).toBeInTheDocument();
|
expect(screen.queryByText('yes2')).toBeInTheDocument();
|
||||||
expect(screen.queryByText('yes3')).toBeInTheDocument();
|
expect(screen.queryByText('yes3')).toBeInTheDocument();
|
||||||
expect(screen.queryByText('yes4')).not.toBeInTheDocument();
|
expect(screen.queryByText('yes4')).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByText('yes5')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as queryString from 'query-string';
|
import * as queryString from 'query-string';
|
||||||
|
|
||||||
|
export type FlagContainer = { [key: string]: any };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FlagContext stores feature flags and passes them to consumers
|
* FlagContext stores feature flags and passes them to consumers
|
||||||
*/
|
*/
|
||||||
|
@ -8,7 +10,7 @@ import * as queryString from 'query-string';
|
||||||
/**
|
/**
|
||||||
* Contains a list of all currently-active flags
|
* Contains a list of all currently-active flags
|
||||||
*/
|
*/
|
||||||
flags: string[];
|
flags: FlagContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FlagContext = React.createContext<IFlagContext>({flags: []});
|
const FlagContext = React.createContext<IFlagContext>({flags: []});
|
||||||
|
@ -16,9 +18,9 @@ const FlagContext = React.createContext<IFlagContext>({flags: []});
|
||||||
/**
|
/**
|
||||||
* `useFlags` returns all feature flags.
|
* `useFlags` returns all feature flags.
|
||||||
*
|
*
|
||||||
* @return {Flags[]} flags All project feature flags
|
* @return {FlagContainer} flags All project feature flags
|
||||||
*/
|
*/
|
||||||
const useFlags = () : string[] => {
|
const useFlags = () : FlagContainer => {
|
||||||
const {flags} = React.useContext(FlagContext);
|
const {flags} = React.useContext(FlagContext);
|
||||||
return flags;
|
return flags;
|
||||||
};
|
};
|
||||||
|
@ -39,9 +41,18 @@ interface IURLFlagProviderProps {
|
||||||
**/
|
**/
|
||||||
const URLFlagProvider = ({children, location}: IURLFlagProviderProps) => {
|
const URLFlagProvider = ({children, location}: IURLFlagProviderProps) => {
|
||||||
const flagString = queryString.parse(location.search).flags;
|
const flagString = queryString.parse(location.search).flags;
|
||||||
let flags: string[] = [];
|
const flags : FlagContainer = {};
|
||||||
|
let flagList: string[] = [];
|
||||||
if (flagString && typeof flagString === 'string') {
|
if (flagString && typeof flagString === 'string') {
|
||||||
flags = (flagString as string).split(',');
|
flagList = (flagString as string).split(',');
|
||||||
|
}
|
||||||
|
for (const flag of flagList) {
|
||||||
|
if (flag.includes('=')) {
|
||||||
|
const [key, value] = flag.split('=');
|
||||||
|
flags[key] = value;
|
||||||
|
} else {
|
||||||
|
flags[flag] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
console.log(JSON.stringify(location), JSON.stringify(flags));
|
console.log(JSON.stringify(location), JSON.stringify(flags));
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
// URLS
|
// URLS
|
||||||
export const FEATURE_TILE_BASE_URL = 'https://d2zjid6n5ja2pt.cloudfront.net';
|
export const FEATURE_TILE_BASE_URL = 'https://d2zjid6n5ja2pt.cloudfront.net';
|
||||||
const XYZ_SUFFIX = '{z}/{x}/{y}.pbf';
|
const XYZ_SUFFIX = '{z}/{x}/{y}.pbf';
|
||||||
export const FEATURE_TILE_HIGH_ZOOM_URL = `${FEATURE_TILE_BASE_URL}/0629_demo/${XYZ_SUFFIX}`;
|
export const featureURLForTilesetName = (tilesetName :string ) : string => {
|
||||||
export const FEATURE_TILE_LOW_ZOOM_URL = `${FEATURE_TILE_BASE_URL}/tiles_low/${XYZ_SUFFIX}`;
|
return `${FEATURE_TILE_BASE_URL}/${tilesetName}/${XYZ_SUFFIX}`;
|
||||||
|
};
|
||||||
|
export const FEATURE_TILE_HIGH_ZOOM_URL = featureURLForTilesetName('0629_demo');
|
||||||
|
export const FEATURE_TILE_LOW_ZOOM_URL = featureURLForTilesetName('tiles_low');
|
||||||
|
|
||||||
|
|
||||||
// Performance markers
|
// Performance markers
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {Style, FillPaint} from 'maplibre-gl';
|
import {Style, FillPaint} from 'maplibre-gl';
|
||||||
import chroma from 'chroma-js';
|
import chroma from 'chroma-js';
|
||||||
import * as constants from '../data/constants';
|
import * as constants from '../data/constants';
|
||||||
|
import {FlagContainer} from '../contexts/FlagContext';
|
||||||
|
|
||||||
// eslint-disable-next-line require-jsdoc
|
// eslint-disable-next-line require-jsdoc
|
||||||
function hexToHSLA(hex:string, alpha:number) {
|
function hexToHSLA(hex:string, alpha:number) {
|
||||||
|
@ -45,7 +46,8 @@ function makePaint({
|
||||||
|
|
||||||
const imageSuffix = constants.isMobile ? '' : '@2x';
|
const imageSuffix = constants.isMobile ? '' : '@2x';
|
||||||
|
|
||||||
const mapStyle : Style = {
|
export const makeMapStyle = (flagContainer: FlagContainer) : Style => {
|
||||||
|
return {
|
||||||
'version': 8,
|
'version': 8,
|
||||||
'sources': {
|
'sources': {
|
||||||
'carto': {
|
'carto': {
|
||||||
|
@ -77,6 +79,8 @@ const mapStyle : Style = {
|
||||||
// The below line promotes the GEOID10 property to the ID
|
// The below line promotes the GEOID10 property to the ID
|
||||||
'promoteId': constants.GEOID_PROPERTY,
|
'promoteId': constants.GEOID_PROPERTY,
|
||||||
'tiles': [
|
'tiles': [
|
||||||
|
'high_tiles' in flagContainer ?
|
||||||
|
constants.featureURLForTilesetName(flagContainer['high_tiles']) :
|
||||||
constants.FEATURE_TILE_HIGH_ZOOM_URL,
|
constants.FEATURE_TILE_HIGH_ZOOM_URL,
|
||||||
],
|
],
|
||||||
// Seeting maxzoom here enables 'overzooming'
|
// Seeting maxzoom here enables 'overzooming'
|
||||||
|
@ -93,6 +97,8 @@ const mapStyle : Style = {
|
||||||
'type': 'vector',
|
'type': 'vector',
|
||||||
'promoteId': constants.GEOID_PROPERTY,
|
'promoteId': constants.GEOID_PROPERTY,
|
||||||
'tiles': [
|
'tiles': [
|
||||||
|
'low_tiles' in flagContainer ?
|
||||||
|
constants.featureURLForTilesetName(flagContainer['low_tiles']) :
|
||||||
constants.FEATURE_TILE_LOW_ZOOM_URL,
|
constants.FEATURE_TILE_LOW_ZOOM_URL,
|
||||||
// For local development, use:
|
// For local development, use:
|
||||||
// 'http://localhost:8080/data/tl_2010_bg_with_data/{z}/{x}/{y}.pbf',
|
// 'http://localhost:8080/data/tl_2010_bg_with_data/{z}/{x}/{y}.pbf',
|
||||||
|
@ -211,6 +217,6 @@ const mapStyle : Style = {
|
||||||
'maxzoom': constants.GLOBAL_MAX_ZOOM,
|
'maxzoom': constants.GLOBAL_MAX_ZOOM,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default mapStyle;
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue