Capabilities |
- Preview features |
Architecture |
Wiki |
Contributing |
@@ -264,12 +263,7 @@ By default, the SharePoint Framework Toolkit will use the Node.js version that i
You can use the settings to change which Node.js version manager you want to use. You may choose between `nvm` and `nvs`. If you wish to avoid using a Node.js version manager, you can set the value to `none`
-## 🧪 Preview features
-
-> [!WARNING]
-> Features described in this section are considered as an early beta feature. They may change or be removed in a future major or minor release.
-
-### 1️⃣ SPFx Toolkit GitHub Chat Participant
+### 1️⃣3️⃣ SPFx Toolkit GitHub Chat Participant
![SPFx Toolkit chat](./assets/images/chat-intro.png)
@@ -286,13 +280,7 @@ Currently, we support the following commands:
- `/new` - that may be used to get guidance on how to create a new solution or find and reuse an existing sample from the PnP SPFx sample gallery
- `/code` - that is fine-tuned to provide help in coding your SharePoint Framework project and provides additional boosters like validating the correctness of your SPFx project, scaffolding a CI/CD workflow, or renaming your project, and many more.
-> [!IMPORTANT]
-> In order for this feature to work you need to meet the following requirements:
-> - Use the [Visual Studio Code Insiders](https://code.visualstudio.com/insiders/) release
-> - Use the pre-release version of the [GitHub Copilot Chat](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat) extension
-> - Use latest version of [SPFx Toolkit](https://marketplace.visualstudio.com/items?itemName=m365pnp.viva-connections-toolkit)
-
-[Check out our docs for more details](https://github.com/pnp/vscode-viva/wiki/8.-Preview-features)
+[Check out our docs for more details](https://github.com/pnp/vscode-viva/wiki/8.-SPFx-Toolkit-GitHub-Chat-Participant)
## ⚙️ Architecture
diff --git a/assets/walkthrough/create-new-project.md b/assets/walkthrough/create-new-project.md
index 135b9c3..756bb6c 100644
--- a/assets/walkthrough/create-new-project.md
+++ b/assets/walkthrough/create-new-project.md
@@ -8,10 +8,15 @@ Check it out in action 👇.
![Create new project](../images/scaffolding-form.gif)
-It's possible to scaffold any kind of SPFx project.
+It's possible to scaffold any kind of SPFx project including web parts, extensions, library components, and adaptive card extensions.
![All SPFx project support](../images/scaffolding-support.png)
+- **Web parts** are reusable elements that serve as the building blocks for SharePoint pages. They allow you to create customizable controls that can be used across multiple pages.
+- **Extensions** enable you to extend the SharePoint user experience by adding scripts to pages, modifying views, adding new actions, and altering list form experiences.
+- **Library components** are reusable code elements that can be shared across all the components in the tenant.
+- **Adaptive Card Extensions (ACEs)** allow you to build rich, native extensions for Viva Connections Dashboards and SharePoint Pages.
+
Install additional dependencies with a single click straight from the scaffolding form. Currently we support installing [PnP reusable property pane controls](https://pnp.github.io/sp-dev-fx-property-controls/), [PnP reusable React controls](https://pnp.github.io/sp-dev-fx-controls-react/), and [PnPjs](https://pnp.github.io/pnpjs/).
![Additional dependency step](../images/scaffolding-additional-step.png)
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index c0f1d12..71422cb 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -1,18 +1,19 @@
{
"name": "viva-connections-toolkit",
- "version": "3.3.0",
+ "version": "3.4.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "viva-connections-toolkit",
- "version": "3.3.0",
+ "version": "3.4.0",
"license": "MIT",
"dependencies": {
"@pnp/cli-microsoft365": "6.11.0",
"node-forge": "^1.3.1",
"react-markdown": "^9.0.1",
- "remark-gfm": "^4.0.0"
+ "remark-gfm": "^4.0.0",
+ "use-debounce": "^10.0.1"
},
"devDependencies": {
"@actions/core": "^1.8.2",
@@ -16848,6 +16849,17 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/use-debounce": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.0.1.tgz",
+ "integrity": "sha512-0uUXjOfm44e6z4LZ/woZvkM8FwV1wiuoB6xnrrOmeAEjRDDzTLQNRFtYHvqUsJdrz1X37j0rVGIVp144GLHGKg==",
+ "engines": {
+ "node": ">= 16.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"dev": true,
diff --git a/package.json b/package.json
index 48b0ae3..8b64fd5 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "viva-connections-toolkit",
"displayName": "SharePoint Framework Toolkit",
"description": "SharePoint Framework Toolkit aims to boost your productivity in developing and managing SharePoint Framework solutions helping at every stage of your development flow, from setting up your development workspace to deploying a solution straight to your tenant without the need to leave VS Code and now even create a CI/CD pipeline to introduce automate deployment of your app. This toolkit is provided by the community.",
- "version": "3.3.0",
+ "version": "3.4.0",
"publisher": "m365pnp",
"preview": false,
"homepage": "https://github.com/pnp/vscode-viva",
@@ -260,7 +260,7 @@
"viewsWelcome": [
{
"view": "pnp-view-empty",
- "contents": "Welcome to SharePoint Framework Toolkit\n\n\nTo start create a new SharePoint Framework project. \n[Create a new project](command:spfx-toolkit.createProject)\n\n\n\n\n\nUse a sample to kick off a new project and boost your development.\n[View samples](command:spfx-toolkit.samplesGallery)\n\n\n\n\n\nYou can also open an existing app.\n[Open folder](command:vscode.openFolder)\n\n\n\n\n\nValidate your local workspace and check if you have the required dependencies.\n[Check dependencies](command:spfx-toolkit.checkDependencies)\n\n\n\n\n\nInstall the required dependencies.\n[Install dependencies](command:spfx-toolkit.installDependencies)\n\n\n\nIf you need additional help or resources visit the [extension wiki](https://github.com/pnp/vscode-viva/wiki) for more information or go over the [walkthrough](command:spfx-toolkit.welcome)."
+ "contents": "Welcome to SharePoint Framework Toolkit\n\n\nTo start create a new SharePoint Framework project. \n[Create a new project](command:spfx-toolkit.createProject)\n\n\n\n\n\nUse a sample to kick off a new project and boost your development.\n[View samples](command:spfx-toolkit.samplesGallery)\n\n\n\n\n\nYou can also open an existing app.\n[Open folder](command:vscode.openFolder)\n\n\n\n\n\nValidate your local workspace and check if you have the required dependencies.\n[Check dependencies](command:spfx-toolkit.checkDependencies)\n\n\n\n\n\nInstall the required dependencies.\n[Install dependencies](command:spfx-toolkit.installDependencies)\n\n\n\nLearn more about SharePoint Framework and this toolkit.\n[Walkthrough](command:spfx-toolkit.welcome)\n\n\n\nFor additional help or resources visit the [extension wiki](https://github.com/pnp/vscode-viva/wiki)."
}
],
"commands": [
@@ -473,6 +473,7 @@
"@pnp/cli-microsoft365": "6.11.0",
"node-forge": "^1.3.1",
"react-markdown": "^9.0.1",
- "remark-gfm": "^4.0.0"
+ "remark-gfm": "^4.0.0",
+ "use-debounce": "^10.0.1"
}
}
diff --git a/src/webview/view/components/controls/MultiSelect.tsx b/src/webview/view/components/controls/MultiSelect.tsx
index ad908d1..ea02cac 100644
--- a/src/webview/view/components/controls/MultiSelect.tsx
+++ b/src/webview/view/components/controls/MultiSelect.tsx
@@ -147,6 +147,7 @@ export const MultiSelect: React.FunctionComponent = ({ onChan
option.selected).map((option) => option.key as any)]}
options={options}
styles={getDropdownStyles()}
onChange={onChange}
diff --git a/src/webview/view/components/gallery/DetailsView.tsx b/src/webview/view/components/gallery/DetailsView.tsx
index a2aab6b..9e11b80 100644
--- a/src/webview/view/components/gallery/DetailsView.tsx
+++ b/src/webview/view/components/gallery/DetailsView.tsx
@@ -21,7 +21,7 @@ export const DetailsView: React.FunctionComponent = ({ }: Rea
useEffect(() => {
const { item } = state;
setSample(item);
-
+ window.scrollTo(0, 0);
const url = item.url.replace('github.com', 'raw.githubusercontent.com').replace('/blob', '').replace('/tree', '');
const fetchData = async () => {
diff --git a/src/webview/view/components/gallery/GalleryView.tsx b/src/webview/view/components/gallery/GalleryView.tsx
index 735f1e0..73c73bc 100644
--- a/src/webview/view/components/gallery/GalleryView.tsx
+++ b/src/webview/view/components/gallery/GalleryView.tsx
@@ -1,22 +1,24 @@
import * as React from 'react';
-import { useState } from 'react';
+import { useEffect } from 'react';
import useSamples from '../../hooks/useSamples';
import { List } from './List';
-import { SearchBar } from './SearchBar';
+import { ISelectedFilter, SearchBar } from './SearchBar';
import { NoResults } from './NoResults';
import { GalleryHeader } from './GalleryHeader';
import { GalleryLoader } from './GalleryLoader';
import { IDropdownOption } from '@fluentui/react';
+import useLocalStorage from '../../hooks/useLocalStorage ';
export interface IGalleryViewProps { }
export const GalleryView: React.FunctionComponent = ({ }: React.PropsWithChildren) => {
const [samples, versions, search] = useSamples();
- const [query, setQuery] = useState('');
- const [spfxVersions, setSPFxVersions] = useState();
- const [showOnlyScenarios, setShowOnlyScenarios] = useState(false);
- const [componentTypes, setComponentTypes] = useState();
+ const [selectedFilters, setSelectedFilters] = useLocalStorage('selectedFilters', []);
+ const [query, setQuery] = useLocalStorage('query', '');
+ const [spfxVersions, setSPFxVersions] = useLocalStorage('spfxVersions', []);
+ const [showOnlyScenarios, setShowOnlyScenarios] = useLocalStorage('showOnlyScenarios', false);
+ const [componentTypes, setComponentTypes] = useLocalStorage('componentTypes', []);
const onSearchTextboxChange = (event: any) => {
const input: string = event.target.value;
@@ -24,6 +26,11 @@ export const GalleryView: React.FunctionComponent = ({ }: Rea
search(input, componentTypes ?? [], spfxVersions ?? [], showOnlyScenarios);
};
+ const onClearTextboxChange = () => {
+ setQuery('');
+ search('', componentTypes ?? [], spfxVersions ?? [], showOnlyScenarios);
+ };
+
const onFilterOnlyScenariosChange = () => {
setShowOnlyScenarios(!showOnlyScenarios);
search(query, componentTypes ?? [], spfxVersions ?? [], !showOnlyScenarios);
@@ -33,22 +40,42 @@ export const GalleryView: React.FunctionComponent = ({ }: Rea
let spfxVersionsInput: string[] = [];
if (option?.selected) {
spfxVersionsInput = [...spfxVersions ?? [], option.key as string];
+ setSelectedFilters([...selectedFilters, {
+ key: option.key as string,
+ text: option.key as string,
+ kind: 'spfxVersion'
+ }]);
} else {
spfxVersionsInput = spfxVersions?.filter(componentType => componentType !== option?.key) ?? [];
+ const removedFilter = selectedFilters.filter(filter => filter.key !== option?.key);
+ setSelectedFilters(removedFilter);
}
-
setSPFxVersions(spfxVersionsInput);
search(query, componentTypes ?? [], spfxVersionsInput, showOnlyScenarios);
};
+ const onRemoveFilterBySPFxVersion = (key: string) => {
+ onFilterBySPFxVersionChange(null, { key: key, text: key, selected: false });
+ };
+
+ const onRemoveFilterByComponentType = (key: string) => {
+ onFilterByComponentTypeChange(null, { key: key, text: key, selected: false });
+ };
+
const onFilterByComponentTypeChange = (event: any, option?: IDropdownOption) => {
let componentTypesInput: string[] = [];
if (option?.selected) {
componentTypesInput = [...componentTypes ?? [], option.key as string];
+ setSelectedFilters([...selectedFilters, {
+ key: option.key as string,
+ text: option.text as string,
+ kind: 'componentType'
+ }]);
} else {
componentTypesInput = componentTypes?.filter(componentType => componentType !== option?.key) ?? [];
+ const removedFilter = selectedFilters.filter(filter => filter.key !== option?.key);
+ setSelectedFilters(removedFilter);
}
-
setComponentTypes(componentTypesInput);
search(query, componentTypesInput, spfxVersions ?? [], showOnlyScenarios);
};
@@ -57,6 +84,7 @@ export const GalleryView: React.FunctionComponent = ({ }: Rea
const dropdownOptions: IDropdownOption[] = versions.map(version => ({
key: version,
text: version,
+ selected: selectedFilters.filter(filter => filter.key === version).length > 0
})).filter((value, index, self) =>
value.key !== null &&
index === self.findIndex((v) => v.key === value.key)
@@ -64,6 +92,23 @@ export const GalleryView: React.FunctionComponent = ({ }: Rea
return dropdownOptions;
};
+ useEffect(() => {
+ if (samples !== undefined) {
+ setShowOnlyScenarios(showOnlyScenarios);
+ search(query, componentTypes ?? [], spfxVersions ?? [], showOnlyScenarios);
+ }
+ }, [samples]);
+
+ const clearFilters = () => {
+ localStorage.clear();
+ setSelectedFilters([]);
+ setSPFxVersions([]);
+ setComponentTypes([]);
+ setShowOnlyScenarios(false);
+ setQuery('');
+ search('', [], [], false);
+ };
+
return (