Merge pull request #2035 from anoopt/master

New sample : Article content - at a glance
This commit is contained in:
Hugo Bernier 2021-09-08 22:49:10 -04:00 committed by GitHub
commit d3d437a14c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 24802 additions and 0 deletions

34
samples/react-at-a-glance/.gitignore vendored Normal file
View File

@ -0,0 +1,34 @@
# 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
*.scss.d.ts

View File

@ -0,0 +1,12 @@
{
"@microsoft/generator-sharepoint": {
"isCreatingSolution": true,
"environment": "spo",
"version": "1.12.1",
"libraryName": "react-at-a-glance",
"libraryId": "bd5e0004-6f21-4734-bea0-9a1b4e0937c9",
"packageManager": "npm",
"isDomainIsolated": false,
"componentType": "webpart"
}
}

View File

@ -0,0 +1,105 @@
# react-at-a-glance
## Summary
This sample shows a web part to show the first few sentences of an article in a SharePoint site, so that the article can be looked at a glance.
The idea is based of the *At a glance* section of a news in the BBC news app (beta).
### Highlights
- Usage of `SPHttpClient` or `PnP JS`
- Conditional property enabling
- Usage of regex to get the sentences from article content
- Usage of Carousel for mobile view
- Usage of the width property to control web part views (small or learge)
## Demo
### Small section / mobile view
![demo](./assets/demo.gif)
## Screenshot of the article in mobile view
![article-content-mobile](./assets/article-content-mobile.png)
### Large section / desktop view
![article-content](./assets/at_a_glance_large.png)
## Screenshot of the article in desktop
![article-content-desktop](./assets/article-content-desktop.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)
![SharePoint Online](https://img.shields.io/badge/SharePoint-Online-yellow.svg)
![Teams N/A: Untested with Microsoft Teams](https://img.shields.io/badge/Teams-N%2FA-lightgrey.svg "Untested with Microsoft Teams")
![Workbench Hosted: Does not work with local workbench](https://img.shields.io/badge/Workbench-Hosted-yellow.svg "Does not work with local workbench")
## Applies to
- [SharePoint Framework](https://aka.ms/spfx)
- [Microsoft 365 tenant](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
> Get your own free development tenant by subscribing to [Microsoft 365 developer program](http://aka.ms/o365devprogram)
## Prerequisites
None
## Solution
Solution|Author(s)
--------|---------
react-at-a-glance | [Anoop Tatti](https://github.com/anoopt) ([https://linktr.ee/anoopt](https://linktr.ee/anoopt))
## Version history
Version|Date|Comments
-------|----|--------
1.0|September 07, 2021|Initial release
## Minimal Path to Awesome
- Clone this repository
- Ensure that you are at the solution folder
- in the command-line run:
- **npm install**
- **gulp serve**
- Open a news article
- Add `?loadSPFX=true&debugManifestsFile=https://localhost:4321/temp/manifests.js` to the URL
- Add the `At a glance` web part to the page
## Features
The web part also has properties to show custom text instead of article text. Upto 3 custom sentences can be added in the properties.
![custom-props](./assets/custom_props.png)
## References
- [Getting started with SharePoint Framework](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
- [Building for Microsoft teams](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/build-for-teams-overview)
- [Use Microsoft Graph in your solution](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/using-microsoft-graph-apis)
- [Publish SharePoint Framework applications to the Marketplace](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/publish-to-marketplace-overview)
- [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - Guidance, tooling, samples and open-source controls for your Microsoft 365 development
## 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-at-a-glance&authors=@anoopt&title=react-at-a-glance%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-at-a-glance&authors=@anoopt&title=react-at-a-glance%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-at-a-glance&authors=@anoopt&title=react-at-a-glance%20-%20).
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-at-a-glance" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

View File

@ -0,0 +1,81 @@
[
{
"name": "pnp-sp-dev-spfx-web-parts-react-at-a-glance",
"source": "pnp",
"title": "At a Glance",
"shortDescription": "This sample shows a web part to show the first few sentences of an article in a SharePoint site, so that the article can be looked at a glance.",
"url": "https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-at-a-glance",
"longDescription": [
"This sample shows a web part to show the first few sentences of an article in a SharePoint site, so that the article can be looked at a glance.",
"The idea is based of the At a glance section of a news in the BBC news app."
],
"creationDateTime": "2021-09-07",
"updateDateTime": "2021-09-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-at-a-glance/assets/demo.gif",
"alt": "Preview"
},
{
"type": "image",
"order": 100,
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-at-a-glance/assets/article-content-desktop.png",
"alt": "Preview"
},
{
"type": "image",
"order": 100,
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-at-a-glance/assets/article-content-mobile.png",
"alt": "Preview"
},
{
"type": "image",
"order": 100,
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-at-a-glance/assets/article-content.png",
"alt": "Preview"
},
{
"type": "image",
"order": 100,
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-at-a-glance/assets/at_a_glance_large.png",
"alt": "Preview"
},
{
"type": "image",
"order": 100,
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-at-a-glance/assets/custom_props.png",
"alt": "Preview"
}
],
"authors": [
{
"gitHubAccount": "anoopt",
"pictureUrl": "https://github.com/anoopt.png",
"name": "Anoop Tatti"
}
],
"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"
}
]
}
]

View File

@ -0,0 +1,19 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"news-glance-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/newsGlance/NewsGlanceWebPart.js",
"manifest": "./src/webparts/newsGlance/NewsGlanceWebPart.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"NewsGlanceWebPartStrings": "lib/webparts/newsGlance/loc/{locale}.js",
"ControlStrings": "node_modules/@pnp/spfx-controls-react/lib/loc/{locale}.js"
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
"deployCdnPath": "./release/assets/"
}

View File

@ -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": "react-at-a-glance",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -0,0 +1,20 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "react-at-a-glance-client-side-solution",
"id": "bd5e0004-6f21-4734-bea0-9a1b4e0937c9",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"isDomainIsolated": false,
"developer": {
"name": "",
"websiteUrl": "",
"privacyUrl": "",
"termsOfUseUrl": "",
"mpnId": ""
}
},
"paths": {
"zippedPackage": "solution/react-at-a-glance.sppkg"
}
}

View File

@ -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/"
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
"cdnBasePath": "<!-- PATH TO CDN -->"
}

View File

@ -0,0 +1,9 @@
{
"$schema": "https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/master/schema/config.1.1.schema.json",
"cli": {
"isLibraryComponent": false
},
"serve": {
"open": false
}
}

View File

@ -0,0 +1,24 @@
/*
* User webpack settings file. You can add your own settings here.
* Changes from this file will be merged into the base webpack configuration file.
* This file will not be overwritten by the subsequent spfx-fast-serve calls.
*/
// you can add your project related webpack configuration here, it will be merged using webpack-merge module
// i.e. plugins: [new webpack.Plugin()]
const webpackConfig = {
}
// for even more fine-grained control, you can apply custom webpack settings using below function
const transformConfig = function (initialWebpackConfig) {
// transform the initial webpack config here, i.e.
// initialWebpackConfig.plugins.push(new webpack.Plugin()); etc.
return initialWebpackConfig;
}
module.exports = {
webpackConfig,
transformConfig
}

22
samples/react-at-a-glance/gulpfile.js vendored Normal file
View File

@ -0,0 +1,22 @@
'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.`);
var getTasks = build.rig.getTasks;
build.rig.getTasks = function () {
var result = getTasks.call(build.rig);
result.set('serve', result.get('serve-deprecated'));
return result;
};
/* fast-serve */
const { addFastServe } = require("spfx-fast-serve-helpers");
addFastServe(build);
/* end of fast-serve */
build.initialize(require('gulp'));

23798
samples/react-at-a-glance/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,38 @@
{
"name": "react-at-a-glance",
"version": "0.0.1",
"private": true,
"main": "lib/index.js",
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test",
"serve": "gulp bundle --custom-serve --max_old_space_size=4096 && fast-serve"
},
"dependencies": {
"@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",
"@pnp/sp": "^2.8.0",
"@pnp/spfx-controls-react": "3.3.0",
"office-ui-fabric-react": "7.156.0",
"react": "16.9.0",
"react-dom": "16.9.0",
"react-multi-carousel": "^2.6.5"
},
"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",
"spfx-fast-serve-helpers": "~1.12.0"
}
}

View File

@ -0,0 +1 @@
// A file is required to be in the root of the /src directory by the TypeScript compiler

View File

@ -0,0 +1,6 @@
export interface IArticle {
title?: string;
link?: string;
imageUrl?: string;
content?: string;
}

View File

@ -0,0 +1,4 @@
export interface INewsGlanceCardProps {
imageUrl?: string;
title?: string;
}

View File

@ -0,0 +1,5 @@
export interface INewsGlanceLargeProps {
showImage: boolean;
imageUrl: string;
sentences: string[];
}

View File

@ -0,0 +1,5 @@
export interface INewsGlanceSmallProps {
showImage: boolean;
imageUrl: string;
sentences: string[];
}

View File

@ -0,0 +1,4 @@
export { IArticle } from './IArticle';
export { INewsGlanceLargeProps } from './INewsGlanceLargeProps';
export { INewsGlanceSmallProps } from './INewsGlanceSmallProps';
export { INewsGlanceCardProps } from './INewsGlanceCardProps';

View File

@ -0,0 +1,83 @@
import { extendFactory } from "@pnp/odata";
import { SPRest } from "@pnp/sp";
import { Web, IWeb } from "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";
import "@pnp/sp/comments";
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { IArticle } from "./interfaces";
declare module "@pnp/sp/webs" {
interface IWeb {
getArticle: (this: IWeb, id: number) => Promise<IArticle>;
}
}
// extend the web factory to add our needed methods
extendFactory(Web, {
getArticle: async function (this: IWeb, id: number): Promise<IArticle> {
try {
const article = await this.lists.getByTitle("Site Pages").items
.getById(id)
.select("Title", "BannerImageUrl", "FileRef", "CanvasContent1")
<{
Title: string,
BannerImageUrl: {
Url: string
},
FileRef: string,
CanvasContent1: string
}>();
return {
title: article.Title,
link: article.FileRef,
imageUrl: article.BannerImageUrl?.Url,
content: article.CanvasContent1
};
} catch (error) {
return null;
}
}
});
let _context: WebPartContext | null = null;
let _sp: SPRest | null = null;
// a method we can use across the application to get a valid sp object, even when
// we no longer have access to the context, such as within views. This must be called
// the first time from the core ACE class to capture a ref to the context
export function getSP(context: WebPartContext = _context): SPRest {
if (typeof _sp !== "undefined" && _sp !== null) {
return _sp;
}
if (_context === null) {
_context = context;
}
if (typeof _context === "undefined" || _context === null) {
throw Error("You must call getSP passing the context within the Extension class before using it child views.");
}
const sp = new SPRest();
// setup our sp as needed for this application
sp.setup({
spfxContext: context,
sp: {
headers: {
"Accept": "application/json;odata=nometadata",
},
},
});
_sp = sp;
return sp;
}

View File

@ -0,0 +1,28 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "a869678a-45e9-401b-a58f-46bd4460c175",
"alias": "NewsGlanceWebPart",
"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": "At a glance" },
"description": { "default": "Shows the first n lines of a news article" },
"officeFabricIconFontName": "View",
"properties": {
"showImage": true,
"numberOfSentences": 3
}
}]
}

View File

@ -0,0 +1,153 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
IPropertyPaneConfiguration,
PropertyPaneTextField,
PropertyPaneToggle
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import { SPHttpClient, SPHttpClientConfiguration, SPHttpClientResponse, ODataVersion, ISPHttpClientConfiguration } from '@microsoft/sp-http';
import * as strings from 'NewsGlanceWebPartStrings';
import NewsGlanceLarge from './components/NewsGlanceLarge';
import { IArticle, INewsGlanceLargeProps, INewsGlanceSmallProps } from '../../interfaces';
import { getSP } from '../../pnpjs';
import NewsGlanceSmall from './components/NewsGlanceSmall';
export interface INewsGlanceWebPartProps {
showImage: boolean;
numberOfSentences: number;
showStaticContent: boolean;
firstContent: string;
secondContent: string;
thirdContent: string;
}
export default class NewsGlanceWebPart extends BaseClientSideWebPart<INewsGlanceWebPartProps> {
private imageUrl: string = "";
private sentences: string[] = [];
private async loadDetails(): Promise<void> {
const spHttpClient: SPHttpClient = this.context.spHttpClient;
const currentWebUrl: string = this.context.pageContext.web.absoluteUrl;
const response = await spHttpClient.get(
`${currentWebUrl}/_api/web/lists/getbytitle('Site Pages')/items(${this.context.pageContext.listItem.id})?$select=Title,BannerImageUrl,FileRef,CanvasContent1`,
SPHttpClient.configurations.v1);
const currentPageDetails = await response.json();
const article: IArticle = {
content: currentPageDetails.CanvasContent1,
imageUrl: currentPageDetails.BannerImageUrl.Url,
link: currentPageDetails.FileRef,
title: currentPageDetails.Title
};
this.imageUrl = article.imageUrl;
if(this.properties.showStaticContent) {
this.sentences = [
this.properties.firstContent,
this.properties.secondContent,
this.properties.thirdContent
];
return;
}
//remove html tags
let articleContent = article.content.replace(/(<([^>]+)>)/gi, "");
//remove any GUIDs
articleContent = articleContent.replace(/(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}/g, "");
let articleContentSentences: string[] = articleContent.match(/([^ \r\n][^!?\.\r\n]+[\w!?\.]+)/g);
this.sentences = articleContentSentences?.slice(0, this.properties.numberOfSentences);
}
public async render(): Promise<void> {
await this.loadDetails();
let element: any;
if (this.width < 400) {
element = React.createElement(
NewsGlanceSmall,
{
showImage: this.properties.showImage,
imageUrl: this.imageUrl,
sentences: this.sentences
}
);
} else {
element = React.createElement(
NewsGlanceLarge,
{
showImage: this.properties.showImage,
imageUrl: this.imageUrl,
sentences: this.sentences
}
);
}
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: "Properties"
},
groups: [
{
groupName: "Settings",
groupFields: [
PropertyPaneToggle('showImage', {
label: "Show image"
}),
PropertyPaneTextField('numberOfSentences', {
label: "Number of sentences to get",
disabled: this.properties.showStaticContent
}),
PropertyPaneToggle('showStaticContent', {
label: "Show static content"
}),
PropertyPaneTextField('firstContent', {
label: "First summary sentence",
multiline: true,
disabled: !this.properties.showStaticContent
}),
PropertyPaneTextField('secondContent', {
label: "Second summary sentence",
multiline: true,
disabled: !this.properties.showStaticContent
}),
PropertyPaneTextField('thirdContent', {
label: "Third summary sentence",
multiline: true,
disabled: !this.properties.showStaticContent
})
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,23 @@
@import '~office-ui-fabric-react/dist/sass/References.scss';
.newsGlancecard {
.wrapper {
margin-top: 0px;
}
.textWrapper{
padding: 10%;
}
.image {
width: 100%;
}
.title {
font-size: 18px;
color: $ms-color-themeLighter;
}
}

View File

@ -0,0 +1,22 @@
import * as React from 'react';
import { INewsGlanceCardProps } from '../../../interfaces';
import styles from './NewsGlanceCard.module.scss';
const NewsGlanceCard: React.FunctionComponent<INewsGlanceCardProps> = (props) => {
return (
<div className={styles.newsGlancecard}>
<div className={styles.wrapper}>
{
props.imageUrl ?
<img src={props.imageUrl} className={styles.image} /> :
<div className={styles.textWrapper}>
<h3 className={styles.title}>{props.title}</h3>
</div>
}
</div>
</div>
);
};
export default NewsGlanceCard;

View File

@ -0,0 +1,50 @@
@import '~office-ui-fabric-react/dist/sass/References.scss';
.newsGlanceLarge {
.container {
border: 1px solid $ms-color-themeDark;
border-radius: 5px;
display: flex;
}
.glanceTitleContainer {
display: flex;
margin-bottom: 5px;
.glanceIcon {
margin: 5px;
}
.glanceTitle {
margin: 0px;
}
}
.item {
padding: 10px;
}
.leftItem {
max-width: 150px;
object-fit: contain;
align-self: flex-start;
}
.rightItem {
flex: 1 1 auto;
}
.articleImage {
width: 100%;
border-radius: 5px;
}
.ulGlance {
list-style: circle;
margin: 0px;
}
.liGlance {
@include ms-font-l;
}
}

View File

@ -0,0 +1,36 @@
import * as React from 'react';
import styles from './NewsGlanceLarge.module.scss';
import { INewsGlanceLargeProps } from '../../../interfaces';
import NewsGlanceTitle from './NewsGlanceTitle';
const NewsGlanceLarge: React.FunctionComponent<INewsGlanceLargeProps> = (props) => {
return (
<div className={styles.newsGlanceLarge}>
<NewsGlanceTitle title="At a glance" />
<div className={styles.container}>
{/* <div className={styles.row}> */}
{
props.showImage &&
<div className={`${styles.item} ${styles.leftItem}`}>
<img className={styles.articleImage} src={props.imageUrl} />
</div>
}
<div className={`${styles.item} ${styles.rightItem}`}>
<ul className={styles.ulGlance}>
{
props.sentences.map((s) => {
return <li className={styles.liGlance}>{s}</li>;
})
}
</ul>
</div>
{/* </div> */}
</div>
</div>
);
};
export default NewsGlanceLarge;

View File

@ -0,0 +1,22 @@
@import '~office-ui-fabric-react/dist/sass/References.scss';
.newsGlanceSmall {
.container {
height: auto;
}
.carouselItem {
background-color: $ms-color-themeDark;
// display: flex;
min-height: 150px;
height: 98%;
// justify-content: center;
}
.carouselDotList {
position: relative;
margin-top: 10px;
}
}

View File

@ -0,0 +1,64 @@
import * as React from 'react';
import { INewsGlanceSmallProps } from '../../../interfaces';
import NewsGlanceCard from './NewsGlanceCard';
import styles from './NewsGlanceSmall.module.scss';
// import { Carousel, CarouselButtonsDisplay, CarouselButtonsLocation, CarouselIndicatorShape } from "@pnp/spfx-controls-react/lib/Carousel";
import Carousel from "react-multi-carousel";
import "react-multi-carousel/lib/styles.css";
import NewsGlanceTitle from './NewsGlanceTitle';
const NewsGlanceSmall: React.FunctionComponent<INewsGlanceSmallProps> = (props) => {
let imageElement: JSX.Element =
<div className={`${styles.carouselItem}`}>
<NewsGlanceCard imageUrl={props.imageUrl} />
</div>;
let sentenceElements: JSX.Element[] =
props.sentences.map((s) => {
return <div className={`${styles.carouselItem}`} >
<NewsGlanceCard title={s} />
</div>;
});
const responsive = {
desktop: {
breakpoint: { max: 3000, min: 1024 },
items: 1
},
tablet: {
breakpoint: { max: 1024, min: 464 },
items: 1
},
mobile: {
breakpoint: { max: 464, min: 0 },
items: 1
}
};
return (
<div className={styles.newsGlanceSmall}>
<NewsGlanceTitle title="At a glance" />
{/* <Carousel
buttonsLocation={CarouselButtonsLocation.center}
buttonsDisplay={CarouselButtonsDisplay.buttonsOnly}
contentContainerStyles={styles.container}
isInfinite={false}
indicatorShape={CarouselIndicatorShape.circle}
interval={null}
element={props.showImage ? [imageElement, ...sentenceElements] : [...sentenceElements]}>
</Carousel> */}
<Carousel
responsive={responsive}
containerClass={styles.container}
showDots={true}
renderDotsOutside={true}
dotListClass={styles.carouselDotList}
removeArrowOnDeviceType={['mobile', 'tablet', 'desktop']}>
{props.showImage && imageElement}
{sentenceElements}
</Carousel>
</div>
);
};
export default NewsGlanceSmall;

View File

@ -0,0 +1,12 @@
.glanceTitleContainer {
display: flex;
margin-bottom: 5px;
.glanceIcon {
margin: 5px;
}
.glanceTitle {
margin: 0px;
}
}

View File

@ -0,0 +1,15 @@
import * as React from 'react';
import { Icon } from 'office-ui-fabric-react/lib/Icon';
import styles from './NewsGlanceTitle.module.scss';
const NewsGlanceTitle: React.FunctionComponent<{ title: string }> = (props) => {
return (
<div className={styles.glanceTitleContainer}>
<Icon className={styles.glanceIcon} iconName="View" />
<h3 className={styles.glanceTitle}> {props.title}</h3>
</div>
);
};
export default NewsGlanceTitle;

View File

@ -0,0 +1,7 @@
define([], function() {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"DescriptionFieldLabel": "Description Field"
}
});

View File

@ -0,0 +1,10 @@
declare interface INewsGlanceWebPartStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
}
declare module 'NewsGlanceWebPartStrings' {
const strings: INewsGlanceWebPartStrings;
export = strings;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

View File

@ -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"
]
}

View File

@ -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
}
}