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

Dashboard: support filtering stories by taxonomy #11625

Merged
merged 66 commits into from
Jun 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
da98445
Starting to involve the category in the dropdown
sblinde May 18, 2022
c6cb7b0
Added Story filtering by category
mwritter May 18, 2022
95b843e
Added karma test for myStories filter by category
mwritter May 19, 2022
ac9ff36
Renamed api
mwritter May 19, 2022
1afc8dd
Fix test
mwritter May 19, 2022
ac8bee4
eslint
mwritter May 19, 2022
fb8b84f
off
mwritter May 19, 2022
b5f6cc2
eslint
mwritter May 19, 2022
611b91a
Changing categories to taxonomies and starting next step of filtering…
sblinde May 20, 2022
a286ecd
Updated dropdown to use all hierarchical taxonomies
mwritter May 23, 2022
b39bfe9
Fixing taxonomies search within filter
mwritter May 23, 2022
f90d823
Adding filters provider, refactory needed
mwritter May 24, 2022
a3804b4
off
mwritter May 24, 2022
97548ba
working provider
mwritter May 25, 2022
6b9ffe7
Code clean up
mwritter May 25, 2022
5d0204e
clean up
mwritter May 25, 2022
96f0347
oof
mwritter May 25, 2022
832be90
Re-scoped FiltersProvider to just MyStories component
mwritter May 26, 2022
402433d
fixes
mwritter May 26, 2022
9c3a55d
Fixing tests
mwritter May 26, 2022
fde1c7d
Added taxonomies api to dashboard preload
mwritter May 26, 2022
26f56a4
oof
mwritter May 26, 2022
0fccee9
delete
mwritter May 26, 2022
7624230
Adding test coverage to reducer
mwritter May 26, 2022
0f40c8d
Updating reducer test
sblinde May 26, 2022
7f245fe
Fixing conflict
sblinde May 26, 2022
d5fd9e6
Added Dynamic taxonomy filters
mwritter May 31, 2022
6e4d20a
Merge branch 'feature/11436-Filter-By-Taxonomy' of github.com:GoogleF…
mwritter May 31, 2022
c54aadb
Merge branch 'main' into feature/11436-Filter-By-Taxonomy
spacedmonkey Jun 1, 2022
6732739
FiltersProvider clean up
mwritter Jun 1, 2022
000d634
Merge branch 'feature/11436-Filter-By-Taxonomy' of github.com:GoogleF…
mwritter Jun 1, 2022
cf5b59c
Clean up
mwritter Jun 1, 2022
dd415de
Updated useTaxonomyFilters documentation and test
mwritter Jun 2, 2022
088f730
Adding documentation, clean up
mwritter Jun 2, 2022
633c4ad
eslint, test coverage
mwritter Jun 2, 2022
5af3013
Used sprintf to for i18n
mwritter Jun 3, 2022
47d2fd8
refactor
mwritter Jun 6, 2022
f4199ae
i18n lint
mwritter Jun 6, 2022
c4fca67
i18n lint
mwritter Jun 6, 2022
d3dd0e1
Updated test
mwritter Jun 6, 2022
5e63aef
Replace i18n calls
mwritter Jun 6, 2022
fe979de
Added test coverage
mwritter Jun 7, 2022
3d67407
oof
mwritter Jun 7, 2022
feada50
tests
mwritter Jun 7, 2022
b300a7e
Merge branch 'main' into feature/11436-Filter-By-Taxonomy
spacedmonkey Jun 8, 2022
e597365
Addressing concerns
mwritter Jun 8, 2022
7975f15
Merge branch 'feature/11436-Filter-By-Taxonomy' of github.com:GoogleF…
mwritter Jun 8, 2022
e4207b4
Merge branch 'main' into feature/11436-Filter-By-Taxonomy
mwritter Jun 8, 2022
ec4d7fb
adding the offsetOverride for PR #11632
mwritter Jun 8, 2022
66f0524
added sprintf
mwritter Jun 8, 2022
a7542bc
i18n valid-sprintf
mwritter Jun 8, 2022
6285ad8
fixed test
mwritter Jun 8, 2022
fba6364
fixed test
mwritter Jun 8, 2022
a9332ff
eslint
mwritter Jun 8, 2022
97c87cd
pr concerns
mwritter Jun 9, 2022
e5feb38
Merge branch 'main' into feature/11436-Filter-By-Taxonomy
mwritter Jun 9, 2022
ee4ff49
added extra css for placeholder, fixed test
mwritter Jun 9, 2022
305dc9d
Fix taxonomies endpoint preloading
swissspidy Jun 10, 2022
9ba2ae5
Merge branch 'main' into feature/11436-Filter-By-Taxonomy
mwritter Jun 10, 2022
b8036a3
Fetch taxonomy terms after filters are initilized with empty data
mwritter Jun 10, 2022
df5ba91
Fetch taxonomy terms after filters are initilized with empty data
mwritter Jun 10, 2022
370aef0
fixing tests and eslint
mwritter Jun 10, 2022
58be966
Merge branch 'main' into feature/11436-Filter-By-Taxonomy
spacedmonkey Jun 13, 2022
617f533
Addressing PR concerns
mwritter Jun 13, 2022
8b2ef7d
Merge branch 'feature/11436-Filter-By-Taxonomy' of github.com:GoogleF…
mwritter Jun 13, 2022
4810f7e
Simplified term fetching logic
mwritter Jun 13, 2022
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
8 changes: 8 additions & 0 deletions includes/Admin/Dashboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,13 @@ public function load_stories_dashboard(): void {
'/web-stories/v1/settings/',
'/web-stories/v1/publisher-logos/',
'/web-stories/v1/users/me/',
'/web-stories/v1/taxonomies/?' . build_query(
[
'type' => $this->story_post_type->get_slug(),
'context' => 'edit',
'hierarchical' => 'true',
]
),
$rest_url . '?' . build_query(
[
'_embed' => rawurlencode(
Expand Down Expand Up @@ -456,6 +463,7 @@ public function get_dashboard_settings(): array {
'settings' => '/web-stories/v1/settings/',
'pages' => '/wp/v2/pages/',
'publisherLogos' => '/web-stories/v1/publisher-logos/',
'taxonomies' => '/web-stories/v1/taxonomies/',
'products' => '/web-stories/v1/products/',
],
'vendors' => $vendors,
Expand Down
3 changes: 3 additions & 0 deletions packages/dashboard/src/app/api/apiProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ import { createContext } from '@googleforcreators/react';
import useStoryApi from './useStoryApi';
import useTemplateApi from './useTemplateApi';
import useUsersApi from './useUsersApi';
import useTaxonomyApi from './useTaxonomyApi';

export const ApiContext = createContext({ state: {}, actions: {} });

export default function ApiProvider({ children }) {
const { api: usersApi } = useUsersApi();
const { api: taxonomyApi } = useTaxonomyApi();
const { templates, api: templateApi } = useTemplateApi();
const { stories, api: storyApi } = useStoryApi();

Expand All @@ -43,6 +45,7 @@ export default function ApiProvider({ children }) {
storyApi,
templateApi,
usersApi,
taxonomyApi,
},
};

Expand Down
34 changes: 34 additions & 0 deletions packages/dashboard/src/app/api/useTaxonomyApi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Internal dependencies
*/
import { useConfig } from '../config';

function useTaxonomyApi() {
const {
apiCallbacks: { getTaxonomies, getTaxonomyTerms },
} = useConfig();

return {
api: {
getTaxonomies,
getTaxonomyTerms,
},
};
}

export default useTaxonomyApi;
134 changes: 134 additions & 0 deletions packages/dashboard/src/app/views/myStories/filters/provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* External dependencies
*/
import {
createContext,
useReducer,
useEffect,
useCallback,
useMemo,
} from '@googleforcreators/react';
import PropTypes from 'prop-types';

/** @typedef {import('react')} Node */

/**
* Internal dependencies
*/
import reducer from './reducer';
import useTaxonomyFilters from './taxonomy/useTaxonomyFilters';
import * as types from './types';

export const filterContext = createContext({
state: {},
actions: {},
});

/**
* Keeps track of the current filters state.
*
* Each filter will have its own logic associated with
* initilization and how to query terms.
*
*
* @param {Object} root0 props for the provider
* @param {Node} root0.children the children to be rendered
* @return {Node} React node
*/

export default function FiltersProvider({ children }) {
// each filter type will have its own logic for initilizing and querying
const { initializeTaxonomyFilters } = useTaxonomyFilters();

const [state, dispatch] = useReducer(reducer, {
filtersLoading: true,
filters: [],
});

/**
* Dispatch UPDATE_FILTER with new data for a given filter
*
* @param {string} key key property on one of the filter objects
* @param {Object} value the properties with updated values
* @return {void}
*/
const updateFilter = useCallback((key, value) => {
dispatch({ type: types.UPDATE_FILTER, payload: { key, value } });
}, []);

/**
* Dispatch REGISTER_FILTERS with all the filters data
*
* @param {Array} payload array of filters data
* @return {void}
*/
const registerFilters = useCallback((value) => {
dispatch({ type: types.REGISTER_FILTERS, payload: { value } });
}, []);

/**
* Sets up the shape of the filters data
* and calls registerFilters with all filters.
*
* @return {void}
*/
const initializeFilters = useCallback(() => {
const filters = initializeTaxonomyFilters();

registerFilters(filters);
}, [registerFilters, initializeTaxonomyFilters]);

/**
* Returns a object where the keys are the filter keys
* and the values are the filterId
*
* @return {Object}
*/
const getFiltersObject = useCallback(() => {
const filterObj = {};
for (const filter of state.filters) {
const { key, filterId } = filter;
if (filterId) {
filterObj[key] = filterId;
}
}
return filterObj;
}, [state.filters]);

const contextValue = useMemo(() => {
return {
state,
actions: { updateFilter, registerFilters, getFiltersObject },
};
}, [state, updateFilter, registerFilters, getFiltersObject]);

useEffect(() => {
initializeFilters();
}, [initializeFilters]);

return (
<filterContext.Provider value={contextValue}>
{children}
</filterContext.Provider>
);
}

FiltersProvider.propTypes = {
children: PropTypes.node,
};
73 changes: 73 additions & 0 deletions packages/dashboard/src/app/views/myStories/filters/reducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* Internal dependencies
*/
import * as types from './types';

/**
* Update the filters state
*
* TODO: May need updating to handle all filter types within the dashboard.
*
* @param {Object} state Current state
* @param {Object} payload Action payload
* @param {string} payload.key Key for associated filter.
* @param {Object} payload.value Value to set on the filter.
* @return {Object} New state
*/

const reducer = (state, { type, payload = {} }) => {
switch (type) {
case types.UPDATE_FILTER: {
const { key, value } = payload;
const filter = state.filters.find((f) => f.key === key);

if (!filter) {
return state;
}

// remove 'filter-by' value
if (value.filterId && filter?.filterId === value.filterId) {
value.filterId = null;
}

// replace old filter
const idx = state.filters.indexOf(filter);
const filters = [...state.filters];
filters[idx] = { ...filter, ...value };

return {
...state,
filters,
};
}

case types.REGISTER_FILTERS: {
const { value } = payload;
return {
...state,
filtersLoading: false,
filters: value,
};
}
default:
return state;
}
};

export default reducer;
Loading