Merge pull request #2070 from karamem0/main
Added react-star-ratings sample
This commit is contained in:
commit
87682a1a38
|
@ -0,0 +1,18 @@
|
||||||
|
# https://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
max_line_length = 80
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
max_line_length = 0
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[COMMIT_EDITMSG]
|
||||||
|
max_line_length = 0
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Build generated files
|
||||||
|
dist
|
||||||
|
lib
|
||||||
|
release
|
||||||
|
solution
|
||||||
|
temp
|
||||||
|
*.sppkg
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# OSX
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Visual Studio files
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
.vs
|
||||||
|
bin
|
||||||
|
obj
|
||||||
|
|
||||||
|
# Resx Generated Code
|
||||||
|
*.resx.ts
|
||||||
|
|
||||||
|
# Styles Generated Code
|
||||||
|
*.scss.ts
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"@microsoft/generator-sharepoint": {
|
||||||
|
"isCreatingSolution": true,
|
||||||
|
"environment": "spo",
|
||||||
|
"version": "1.12.1",
|
||||||
|
"libraryName": "ratings",
|
||||||
|
"libraryId": "1fac5bb8-6a05-4c52-aee3-496f6921ac1a",
|
||||||
|
"packageManager": "npm",
|
||||||
|
"isDomainIsolated": true,
|
||||||
|
"componentType": "webpart"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
# Star Ratings
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This web part demonstrates *Star Ratings* capablities to SharePoint News. The "Ratings" site collection feature provides *Likes* and *Star Ratings*, but SharePoint News provides only provides *Likes*. This web part can get or set ratings of the current page.
|
||||||
|
|
||||||
|
![react-star-ratings](./assets/react-star-ratings.png)
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
![SPFx 1.12.1](https://img.shields.io/badge/SPFx-1.12.1-green.svg)
|
||||||
|
![Node.js LTS v14 | LTS v12 | LTS v10](https://img.shields.io/badge/Node.js-LTS%20v14%20%7C%20LTS%20v12%20%7C%20LTS%20v10-green.svg)
|
||||||
|
![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
|
||||||
|
![Does not work with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Incompatible-red.svg "SharePoint Server 2019 requires SPFx 1.4.1 or lower")
|
||||||
|
![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Incompatible-red.svg "SharePoint Server 2016 Feature Pack 2 requires SPFx 1.1")
|
||||||
|
![Local Workbench Incompatible](https://img.shields.io/badge/Local%20Workbench-Incompatible-red.svg "This solution requires the ratings feature from SharePoint Online")
|
||||||
|
![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg)
|
||||||
|
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. Enable the "Ratings" feature of the site collection. The feature GUID is `915c240e-a6cc-49b8-8b2c-0bff8b553ed3`.
|
||||||
|
|
||||||
|
```
|
||||||
|
Enable-PnPFeature -Identity 915c240e-a6cc-49b8-8b2c-0bff8b553ed3
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Update "Rating settings".
|
||||||
|
|
||||||
|
- Select **⚙** - **Site contents**.
|
||||||
|
- Select **Site Pages** - **︙** - **Settings**.
|
||||||
|
- Select **Rating settings**.
|
||||||
|
- Update values.
|
||||||
|
- **Rating Settings**: Select **Yes**
|
||||||
|
- **Which voting/rating experience you would like to enable for this list?**: Select **Star Ratings**
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
Solution|Author(s)
|
||||||
|
--------|---------
|
||||||
|
react-star-ratings | [Takashi Shinohara](https://github.com/karamem0) ([@karamem0](https://twitter.com/karamem0))
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
Version|Date|Comments
|
||||||
|
-------|----|--------
|
||||||
|
1.0|October 7, 2021|Initial release
|
||||||
|
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||||
|
|
||||||
|
## Help
|
||||||
|
|
||||||
|
We do not support samples, but we this community is always willing to help, and we want to improve these samples. We use GitHub to track issues, which makes it easy for community members to volunteer their time and help resolve issues.
|
||||||
|
|
||||||
|
If you encounter any issues while using this sample, [create a new issue](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=bug-report.yml&sample=react-star-ratings&authors=@karamem0&title=react-star-ratings%20-%20).
|
||||||
|
|
||||||
|
For questions regarding this sample, [create a new question](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=question.yml&sample=react-star-ratings&authors=@karamem0&title=react-star-ratings%20-%20).
|
||||||
|
|
||||||
|
Finally, if you have an idea for improvement, [make a suggestion](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=suggestion.yml&sample=react-star-ratings&authors=@karamem0&title=react-star-ratings%20-%20).
|
||||||
|
|
||||||
|
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-star-ratings" />
|
Binary file not shown.
After Width: | Height: | Size: 121 KiB |
|
@ -0,0 +1,50 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "pnp-sp-dev-spfx-web-parts-react-star-ratings",
|
||||||
|
"source": "pnp",
|
||||||
|
"title": "Star Ratings",
|
||||||
|
"shortDescription": "Demonstrates Star Ratings capablities to SharePoint News.",
|
||||||
|
"url": "https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-star-ratings",
|
||||||
|
"longDescription": [
|
||||||
|
"This web part demonstrates Star Ratings capablities to SharePoint News. The Ratings site collection feature provides Likes and Star Ratings, but SharePoint News provides only provides Likes. This web part can get or set ratings of the current page."
|
||||||
|
],
|
||||||
|
"creationDateTime": "2021-10-07",
|
||||||
|
"updateDateTime": "2021-10-07",
|
||||||
|
"products": [
|
||||||
|
"SharePoint",
|
||||||
|
"Office"
|
||||||
|
],
|
||||||
|
"metadata": [
|
||||||
|
{
|
||||||
|
"key": "CLIENT-SIDE-DEV",
|
||||||
|
"value": "React"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "SPFX-VERSION",
|
||||||
|
"value": "1.12.1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thumbnails": [
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"order": 100,
|
||||||
|
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-star-ratings/assets/react-star-ratings.png",
|
||||||
|
"alt": "Web part in action"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"gitHubAccount": "karamem0",
|
||||||
|
"pictureUrl": "https://github.com/karamem0.png",
|
||||||
|
"name": "Takashi Shinohara"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Build your first SharePoint client-side web part",
|
||||||
|
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
|
||||||
|
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||||
|
"version": "2.0",
|
||||||
|
"bundles": {
|
||||||
|
"ratings-web-part": {
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"entrypoint": "./lib/webparts/ratings/RatingsWebPart.js",
|
||||||
|
"manifest": "./src/webparts/ratings/RatingsWebPart.manifest.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"externals": {},
|
||||||
|
"localizedResources": {
|
||||||
|
"RatingsWebPartStrings": "lib/webparts/ratings/loc/{locale}.js"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||||
|
"deployCdnPath": "./release/assets/"
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||||
|
"workingDir": "./release/assets/",
|
||||||
|
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||||
|
"container": "ratings",
|
||||||
|
"accessKey": "<!-- ACCESS KEY -->"
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||||
|
"solution": {
|
||||||
|
"name": "ratings",
|
||||||
|
"id": "1fac5bb8-6a05-4c52-aee3-496f6921ac1a",
|
||||||
|
"version": "1.0.0.0",
|
||||||
|
"includeClientSideAssets": true,
|
||||||
|
"skipFeatureDeployment": true,
|
||||||
|
"isDomainIsolated": false,
|
||||||
|
"developer": {
|
||||||
|
"name": "",
|
||||||
|
"websiteUrl": "",
|
||||||
|
"privacyUrl": "",
|
||||||
|
"termsOfUseUrl": "",
|
||||||
|
"mpnId": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"zippedPackage": "solution/ratings.sppkg"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
|
||||||
|
"port": 4321,
|
||||||
|
"https": true,
|
||||||
|
"initialPage": "https://localhost:5432/workbench",
|
||||||
|
"api": {
|
||||||
|
"port": 5432,
|
||||||
|
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||||
|
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const build = require('@microsoft/sp-build-web');
|
||||||
|
|
||||||
|
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
|
||||||
|
build.addSuppression(`Warning - [sass] The local CSS class '-ms-flex' is not camelCase and will not be type-safe.`);
|
||||||
|
|
||||||
|
var getTasks = build.rig.getTasks;
|
||||||
|
build.rig.getTasks = function () {
|
||||||
|
var result = getTasks.call(build.rig);
|
||||||
|
|
||||||
|
result.set('serve', result.get('serve-deprecated'));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
build.initialize(require('gulp'));
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"name": "ratings",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "gulp bundle",
|
||||||
|
"clean": "gulp clean",
|
||||||
|
"test": "gulp test"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@fluentui/react": "^7.177.2",
|
||||||
|
"@microsoft/sp-core-library": "1.12.1",
|
||||||
|
"@microsoft/sp-lodash-subset": "1.12.1",
|
||||||
|
"@microsoft/sp-office-ui-fabric-core": "1.12.1",
|
||||||
|
"@microsoft/sp-property-pane": "1.12.1",
|
||||||
|
"@microsoft/sp-webpart-base": "1.12.1",
|
||||||
|
"office-ui-fabric-react": "7.156.0",
|
||||||
|
"react": "16.9.0",
|
||||||
|
"react-dom": "16.9.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "16.9.36",
|
||||||
|
"@types/react-dom": "16.9.8",
|
||||||
|
"@microsoft/sp-build-web": "1.12.1",
|
||||||
|
"@microsoft/sp-tslint-rules": "1.12.1",
|
||||||
|
"@microsoft/sp-module-interfaces": "1.12.1",
|
||||||
|
"@microsoft/sp-webpart-workbench": "1.12.1",
|
||||||
|
"@microsoft/rush-stack-compiler-3.7": "0.2.3",
|
||||||
|
"gulp": "~4.0.2",
|
||||||
|
"ajv": "~5.2.2",
|
||||||
|
"@types/webpack-env": "1.13.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||||
|
"id": "a427f628-450b-4ee2-ad89-7837d22c8baf",
|
||||||
|
"alias": "RatingsWebPart",
|
||||||
|
"componentType": "WebPart",
|
||||||
|
// The "*" signifies that the version should be taken from the package.json
|
||||||
|
"version": "*",
|
||||||
|
"manifestVersion": 2,
|
||||||
|
// If true, the component can only be installed on sites where Custom Script is allowed.
|
||||||
|
// Components that allow authors to embed arbitrary script code should set this to true.
|
||||||
|
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
|
||||||
|
"requiresCustomScript": false,
|
||||||
|
"supportedHosts": [
|
||||||
|
"SharePointWebPart"
|
||||||
|
],
|
||||||
|
"preconfiguredEntries": [
|
||||||
|
{
|
||||||
|
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
|
||||||
|
"group": {
|
||||||
|
"default": "Other"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"default": "Ratings"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"default": "You can rate site pages."
|
||||||
|
},
|
||||||
|
"officeFabricIconFontName": "FavoriteStar",
|
||||||
|
"properties": {
|
||||||
|
"activeColor": "#ffb900",
|
||||||
|
"inactiveColor": "#d29200"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as ReactDom from 'react-dom';
|
||||||
|
import { Version } from '@microsoft/sp-core-library';
|
||||||
|
import {
|
||||||
|
IPropertyPaneConfiguration,
|
||||||
|
PropertyPaneTextField
|
||||||
|
} from '@microsoft/sp-property-pane';
|
||||||
|
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||||||
|
|
||||||
|
import * as strings from 'RatingsWebPartStrings';
|
||||||
|
import Ratings from './components/Ratings';
|
||||||
|
import { IRatingsProps } from './components/IRatingsProps';
|
||||||
|
|
||||||
|
export interface IRatingsWebPartProps {
|
||||||
|
activeColor: string;
|
||||||
|
inactiveColor: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class RatingsWebPart extends BaseClientSideWebPart<IRatingsWebPartProps> {
|
||||||
|
|
||||||
|
public render(): void {
|
||||||
|
const element: React.ReactElement<IRatingsProps> = React.createElement(
|
||||||
|
Ratings,
|
||||||
|
{
|
||||||
|
context: this.context,
|
||||||
|
properties: this.properties
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ReactDom.render(element, this.domElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onDispose(): void {
|
||||||
|
ReactDom.unmountComponentAtNode(this.domElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get dataVersion(): Version {
|
||||||
|
return Version.parse('1.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||||
|
return {
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
header: {
|
||||||
|
description: strings.PropertyPaneDescription
|
||||||
|
},
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
groupFields: [
|
||||||
|
PropertyPaneTextField('activeColor', {
|
||||||
|
label: strings.ActiveColorLabel
|
||||||
|
}),
|
||||||
|
PropertyPaneTextField('inactiveColor', {
|
||||||
|
label: strings.InactiveColorLabel
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
||||||
|
import { IRatingsWebPartProps } from '../RatingsWebPart';
|
||||||
|
|
||||||
|
export interface IRatingsProps {
|
||||||
|
context: WebPartContext;
|
||||||
|
properties: IRatingsWebPartProps;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
@import '~office-ui-fabric-react/dist/sass/References.scss';
|
||||||
|
|
||||||
|
.root {
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
gap: .5rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import {
|
||||||
|
MessageBar,
|
||||||
|
MessageBarType,
|
||||||
|
IRatingStyleProps,
|
||||||
|
IRatingStyles,
|
||||||
|
Rating,
|
||||||
|
RatingSize,
|
||||||
|
TooltipHost
|
||||||
|
} from '@fluentui/react';
|
||||||
|
import styles from './Ratings.module.scss';
|
||||||
|
import * as strings from 'RatingsWebPartStrings';
|
||||||
|
import { IRatingsProps } from './IRatingsProps';
|
||||||
|
import SPHttpClientService from '../services/SPHttpClientService';
|
||||||
|
import '../extensions/Map';
|
||||||
|
|
||||||
|
interface IRatings {
|
||||||
|
rating: number;
|
||||||
|
count: number;
|
||||||
|
average: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Ratings = ({ context, properties }: IRatingsProps) => {
|
||||||
|
|
||||||
|
const ratingStyles = React.useMemo(() => (props: IRatingStyleProps): Partial<IRatingStyles> => ({
|
||||||
|
root: {
|
||||||
|
selectors: {
|
||||||
|
'&:hover': {
|
||||||
|
selectors: {
|
||||||
|
'.ms-RatingStar-back': {
|
||||||
|
color: properties.inactiveColor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ratingButton: {
|
||||||
|
selectors: {
|
||||||
|
'&:hover ~ .ms-Rating-button': {
|
||||||
|
selectors: {
|
||||||
|
'.ms-RatingStar-back': {
|
||||||
|
color: properties.inactiveColor,
|
||||||
|
},
|
||||||
|
'.ms-RatingStar-front': {
|
||||||
|
color: properties.inactiveColor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'&:hover': {
|
||||||
|
selectors: {
|
||||||
|
'.ms-RatingStar-back': {
|
||||||
|
color: properties.inactiveColor,
|
||||||
|
},
|
||||||
|
'.ms-RatingStar-front': {
|
||||||
|
color: properties.inactiveColor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ratingStarFront: {
|
||||||
|
color: properties.activeColor
|
||||||
|
},
|
||||||
|
ratingStarBack: {
|
||||||
|
color: properties.activeColor
|
||||||
|
}
|
||||||
|
}), [
|
||||||
|
properties.activeColor,
|
||||||
|
properties.inactiveColor
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [loading, setLoading] = React.useState<boolean>(true);
|
||||||
|
const [error, setError] = React.useState<string>();
|
||||||
|
const [value, setValue] = React.useState<IRatings>();
|
||||||
|
|
||||||
|
const getRating = React.useCallback(async (): Promise<IRatings> => {
|
||||||
|
const service = new SPHttpClientService(context);
|
||||||
|
await service.ensureFeatureEnabled();
|
||||||
|
const user = await service.getCurrentUser();
|
||||||
|
const ratings = await service.getRatings();
|
||||||
|
const rating = ratings.get(user.LoginName);
|
||||||
|
const count = ratings.size;
|
||||||
|
const sum = ratings.values().reduce((current, prev) => prev + current, 0);
|
||||||
|
return {
|
||||||
|
rating: rating,
|
||||||
|
count: count,
|
||||||
|
average: count ? sum / count : 0
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setRating = React.useCallback(async (rating: number): Promise<void> => {
|
||||||
|
const service = new SPHttpClientService(context);
|
||||||
|
await service.setRating(rating);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleOnChange = React.useCallback((_, rating?: number) => {
|
||||||
|
if (!rating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
await setRating(rating);
|
||||||
|
setValue(await getRating());
|
||||||
|
} catch (error) {
|
||||||
|
setError(error.toString());
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, [context]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
setValue(await getRating());
|
||||||
|
} catch (error) {
|
||||||
|
setError(error.toString());
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, [context]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.root}>
|
||||||
|
<div className={styles.container}>
|
||||||
|
{
|
||||||
|
loading
|
||||||
|
? null
|
||||||
|
: error
|
||||||
|
? (
|
||||||
|
<MessageBar messageBarType={MessageBarType.error}>
|
||||||
|
{error}
|
||||||
|
</MessageBar>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<div className={styles.flex}>
|
||||||
|
<span>{strings.RateThisPageLabel}: </span>
|
||||||
|
<TooltipHost content={`${strings.AverageLabel}: ${value.average}, ${strings.CountLabel}: ${value.count}`}>
|
||||||
|
<Rating
|
||||||
|
allowZeroStars
|
||||||
|
rating={value.rating}
|
||||||
|
size={RatingSize.Small}
|
||||||
|
styles={ratingStyles}
|
||||||
|
onChange={handleOnChange} />
|
||||||
|
</TooltipHost>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Ratings;
|
|
@ -0,0 +1,26 @@
|
||||||
|
interface Map<K, V> {
|
||||||
|
keys(): K[];
|
||||||
|
values(): V[];
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(Map.prototype, 'keys', {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: false,
|
||||||
|
writable: true,
|
||||||
|
value: function () {
|
||||||
|
const array = [];
|
||||||
|
this.forEach((value, key) => array.push(key));
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(Map.prototype, 'values', {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: false,
|
||||||
|
writable: true,
|
||||||
|
value: function () {
|
||||||
|
const array = [];
|
||||||
|
this.forEach((value, key) => array.push(value));
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,10 @@
|
||||||
|
define([], function() {
|
||||||
|
return {
|
||||||
|
"PropertyPaneDescription": "You can rate site pages.",
|
||||||
|
"ActiveColorLabel": "Active Color",
|
||||||
|
"InactiveColorLabel": "Inactive Color",
|
||||||
|
"RateThisPageLabel": "Rate this page",
|
||||||
|
"AverageLabel": "Average",
|
||||||
|
"CountLabel": "Count"
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,10 @@
|
||||||
|
define([], function() {
|
||||||
|
return {
|
||||||
|
"PropertyPaneDescription": "サイトのページを評価することができます。",
|
||||||
|
"ActiveColorLabel": "アクティブな色",
|
||||||
|
"InactiveColorLabel": "非アクティブな色",
|
||||||
|
"RateThisPageLabel": "このページの評価",
|
||||||
|
"AverageLabel": "平均",
|
||||||
|
"CountLabel": "件数"
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,13 @@
|
||||||
|
declare interface IRatingsWebPartStrings {
|
||||||
|
PropertyPaneDescription: string;
|
||||||
|
ActiveColorLabel: string;
|
||||||
|
InactiveColorLabel: string;
|
||||||
|
RateThisPageLabel: string;
|
||||||
|
AverageLabel: string;
|
||||||
|
CountLabel: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'RatingsWebPartStrings' {
|
||||||
|
const strings: IRatingsWebPartStrings;
|
||||||
|
export = strings;
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
import { Guid } from '@microsoft/sp-core-library';
|
||||||
|
import { SPHttpClient } from '@microsoft/sp-http';
|
||||||
|
import { WebPartContext } from '@microsoft/sp-webpart-base';
|
||||||
|
|
||||||
|
interface IUser {
|
||||||
|
Id: number;
|
||||||
|
LoginName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRetedBy {
|
||||||
|
Id: string;
|
||||||
|
Name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRating {
|
||||||
|
RatedBy: IRetedBy[];
|
||||||
|
Ratings: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SPSPHttpClientService {
|
||||||
|
|
||||||
|
private client: SPHttpClient;
|
||||||
|
private url: string;
|
||||||
|
private listId: Guid;
|
||||||
|
private itemId: number;
|
||||||
|
|
||||||
|
public constructor(context: WebPartContext) {
|
||||||
|
this.client = context.spHttpClient;
|
||||||
|
this.url = context.pageContext.web.absoluteUrl;
|
||||||
|
this.listId = context.pageContext.list.id;
|
||||||
|
this.itemId = context.pageContext.listItem.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ensureFeatureEnabled(): Promise<void> {
|
||||||
|
const response = await this.client.get(
|
||||||
|
`${this.url}/_api/web/lists('${this.listId}')/rootfolder/properties`,
|
||||||
|
SPHttpClient.configurations.v1);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch data.');
|
||||||
|
}
|
||||||
|
const json = await response.json();
|
||||||
|
const value = json['Ratings_x005f_VotingExperience'];
|
||||||
|
if (value !== 'Ratings') {
|
||||||
|
throw new Error('"Ratings" site collection feature is not enabled.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getCurrentUser(): Promise<IUser> {
|
||||||
|
const response = await this.client.get(
|
||||||
|
`${this.url}/_api/web/currentuser` +
|
||||||
|
'?$select=Id,LoginName',
|
||||||
|
SPHttpClient.configurations.v1);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch data.');
|
||||||
|
}
|
||||||
|
const json = await response.json();
|
||||||
|
const value = json as IUser;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getRatings(): Promise<Map<string, number>> {
|
||||||
|
const response = await this.client.get(
|
||||||
|
`${this.url}/_api/web/lists('${this.listId}')/items(${this.itemId})` +
|
||||||
|
'?$select=Ratings,RatedBy/Id,RatedBy/Name' +
|
||||||
|
'&$expand=RatedBy',
|
||||||
|
SPHttpClient.configurations.v1);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch data.');
|
||||||
|
}
|
||||||
|
const json = await response.json();
|
||||||
|
const value = json as IRating;
|
||||||
|
if (value.Ratings) {
|
||||||
|
return new Map(value.Ratings
|
||||||
|
.slice(0, -1)
|
||||||
|
.split(',')
|
||||||
|
.map(item => Number(item))
|
||||||
|
.map((item, index) => ([value.RatedBy[index].Name, item])));
|
||||||
|
} else {
|
||||||
|
return new Map();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async setRating(rating: number): Promise<void> {
|
||||||
|
const response = await this.client.post(
|
||||||
|
`${this.url}/_api/Microsoft.Office.Server.ReputationModel.Reputation.SetRating(listid=@a1,itemid=@a2,rating=@a3)` +
|
||||||
|
`?@a1='${this.listId}'` +
|
||||||
|
`&@a2=${this.itemId}` +
|
||||||
|
`&@a3=${rating}`,
|
||||||
|
SPHttpClient.configurations.v1,
|
||||||
|
{});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch data.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 383 B |
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.7/includes/tsconfig-web.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"jsx": "react",
|
||||||
|
"declaration": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"outDir": "lib",
|
||||||
|
"inlineSources": false,
|
||||||
|
"strictNullChecks": false,
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"typeRoots": [
|
||||||
|
"./node_modules/@types",
|
||||||
|
"./node_modules/@microsoft"
|
||||||
|
],
|
||||||
|
"types": [
|
||||||
|
"webpack-env"
|
||||||
|
],
|
||||||
|
"lib": [
|
||||||
|
"es5",
|
||||||
|
"dom",
|
||||||
|
"es2015.collection",
|
||||||
|
"es2015.promise"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.tsx"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"extends": "./node_modules/@microsoft/sp-tslint-rules/base-tslint.json",
|
||||||
|
"rules": {
|
||||||
|
"class-name": false,
|
||||||
|
"export-name": false,
|
||||||
|
"forin": false,
|
||||||
|
"label-position": false,
|
||||||
|
"member-access": true,
|
||||||
|
"no-arg": false,
|
||||||
|
"no-console": false,
|
||||||
|
"no-construct": false,
|
||||||
|
"no-duplicate-variable": true,
|
||||||
|
"no-eval": false,
|
||||||
|
"no-function-expression": true,
|
||||||
|
"no-internal-module": true,
|
||||||
|
"no-shadowed-variable": true,
|
||||||
|
"no-switch-case-fall-through": true,
|
||||||
|
"no-unnecessary-semicolons": true,
|
||||||
|
"no-unused-expression": true,
|
||||||
|
"no-use-before-declare": true,
|
||||||
|
"no-with-statement": true,
|
||||||
|
"semicolon": true,
|
||||||
|
"trailing-comma": false,
|
||||||
|
"typedef": false,
|
||||||
|
"typedef-whitespace": false,
|
||||||
|
"use-named-parameter": true,
|
||||||
|
"variable-name": false,
|
||||||
|
"whitespace": false
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue