@adobe/helix-importer 1.2.2 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## [1.4.1](https://github.com/adobe/helix-importer/compare/v1.4.0...v1.4.1) (2022-02-24)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * some images come with wrong src in md ([#16](https://github.com/adobe/helix-importer/issues/16)) ([7f27432](https://github.com/adobe/helix-importer/commit/7f274326683313cd11c2a92bb1c01c1960de5fcd))
7
+
8
+ # [1.4.0](https://github.com/adobe/helix-importer/compare/v1.3.0...v1.4.0) (2022-02-02)
9
+
10
+
11
+ ### Features
12
+
13
+ * add docxStylesXML param support ([1a10784](https://github.com/adobe/helix-importer/commit/1a1078453f1f5d761cf7f64addc013f8b73dc225))
14
+
15
+ # [1.3.0](https://github.com/adobe/helix-importer/compare/v1.2.2...v1.3.0) (2022-01-31)
16
+
17
+
18
+ ### Features
19
+
20
+ * restore custom handlers ([a051c80](https://github.com/adobe/helix-importer/commit/a051c80e18d23c87b845c6fc023e81f788d5e9fc))
21
+
1
22
  ## [1.2.2](https://github.com/adobe/helix-importer/compare/v1.2.1...v1.2.2) (2022-01-25)
2
23
 
3
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/helix-importer",
3
- "version": "1.2.2",
3
+ "version": "1.4.1",
4
4
  "description": "Helix Importer tool: create md / docx from html",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -33,6 +33,7 @@
33
33
  "@semantic-release/git": "10.0.1",
34
34
  "c8": "7.11.0",
35
35
  "codecov": "3.8.3",
36
+ "dirname-filename-esm": "1.1.1",
36
37
  "eslint": "8.7.0",
37
38
  "eslint-plugin-header": "3.1.1",
38
39
  "eslint-plugin-import": "2.25.4",
@@ -46,7 +47,7 @@
46
47
  "author": "",
47
48
  "license": "Apache-2.0",
48
49
  "dependencies": {
49
- "@adobe/helix-md2docx": "1.2.3",
50
+ "@adobe/helix-md2docx": "1.3.0",
50
51
  "form-data": "4.0.0",
51
52
  "fs-extra": "10.0.0",
52
53
  "hast-util-to-html": "8.0.3",
@@ -20,12 +20,12 @@ import parse from 'rehype-parse';
20
20
  import { toHtml } from 'hast-util-to-html';
21
21
  import rehype2remark from 'rehype-remark';
22
22
  import stringify from 'remark-stringify';
23
- import { all } from 'hast-util-to-mdast/lib/all.js';
24
23
  import fs from 'fs-extra';
25
24
  import { md2docx } from '@adobe/helix-md2docx';
26
25
  import Utils from '../utils/Utils.js';
27
26
  import DOMUtils from '../utils/DOMUtils.js';
28
27
  import FileUtils from '../utils/FileUtils.js';
28
+ import MDUtils from '../utils/MDUtils.js';
29
29
 
30
30
  export default class PageImporter {
31
31
  params;
@@ -42,7 +42,7 @@ export default class PageImporter {
42
42
  }
43
43
 
44
44
  async convertToDocx(docxPath, content) {
45
- const buffer = await md2docx(content, this.logger);
45
+ const buffer = await md2docx(content, this.logger, this.params.docxStylesXML);
46
46
  return this.params.storageHandler.put(docxPath, buffer);
47
47
  }
48
48
 
@@ -56,9 +56,18 @@ export default class PageImporter {
56
56
  .use(parse, { emitParseErrors: true })
57
57
  .use(rehype2remark, {
58
58
  handlers: {
59
- hlxembed: (h, node) => h(node, 'hlxembed', node.children[0].value),
60
- u: (h, node) => h(node, 'u', all(h, node)),
61
- table: (h, node) => h(node, 'table', toHtml(node)),
59
+ hlxembed: (h, node) => {
60
+ const children = node.children.map((child) => processor.stringify(child).trim());
61
+ return h(node, 'text', `${children.join()}\n`);
62
+ },
63
+ u: (h, node) => {
64
+ if (node.children && node.children.length > 0) {
65
+ const children = node.children.map((child) => processor.stringify(child).trim());
66
+ return h(node, 'html', `<u>${children.join()}</u>`);
67
+ }
68
+ return '';
69
+ },
70
+ table: (h, node) => h(node, 'html', toHtml(node)),
62
71
  },
63
72
  })
64
73
  .use(stringify, {
@@ -69,36 +78,6 @@ export default class PageImporter {
69
78
  rule: '-',
70
79
  ruleRepetition: 3,
71
80
  ruleSpaces: false,
72
- })
73
- .use(() => {
74
- // use custom tag and rendering because text is always encoded by default
75
- // we need the raw url
76
- // processor.Compiler.prototype.visitors.hlxembed = (node) => node.value;
77
- })
78
- .use(() => {
79
- // processor.Compiler.prototype.visitors.table = (node) => node.value;
80
- })
81
- .use(() => {
82
- // processor.Compiler.prototype.visitors.u = (node) => {
83
- // // u handling: remove the u is the first element is a link
84
- // if (node.children && node.children.length > 0) {
85
- // const children = node.children.map((child) => processor.stringify(child));
86
- // if (node.children[0].type === 'link') {
87
- // // first element in the <u> is a link: remove the <u> - unsupported case
88
- // return `${children.join()}`;
89
- // }
90
- // return `<u>${children.join()}</u>`;
91
- // }
92
- // return '';
93
- // };
94
- })
95
- .use(() => {
96
- // const originalEmphasis = processor.Compiler.prototype.visitors.emphasis;
97
- // processor.Compiler.prototype.visitors.emphasis = (node) => {
98
- // // @ts-ignore
99
- // const ori = originalEmphasis.apply(processor.Compiler(), [node]);
100
- // return ori;
101
- // };
102
81
  });
103
82
 
104
83
  const file = await processor.process(resource.document.innerHTML);
@@ -134,7 +113,7 @@ export default class PageImporter {
134
113
  });
135
114
  }
136
115
  } catch (error) {
137
- this.logger.warn(`Invalid link in the page: ${href}`);
116
+ this.logger.warn(`Invalid link in the page: ${href}`, error);
138
117
  }
139
118
  }
140
119
  });
@@ -160,19 +139,15 @@ export default class PageImporter {
160
139
  });
161
140
  }
162
141
  } catch (error) {
163
- this.logger.warn(`Invalid video in the page: ${src}`);
142
+ this.logger.warn(`Invalid video in the page: ${src}`, error);
164
143
  }
165
144
  }
166
145
  });
167
146
 
168
- const patchSrcInContent = (c, oldSrc, newSrc) => contents
169
- .replace(new RegExp(`${oldSrc.replace('.', '\\.').replace('?', '\\?')}`, 'gm'), newSrc)
170
- .replace(new RegExp(`${decodeURI(oldSrc).replace('.', '\\.')}`, 'gm'), newSrc);
171
-
172
147
  // adjust assets url (from relative to absolute)
173
148
  assets.forEach((asset) => {
174
149
  const u = new URL(decodeURI(asset.url), url);
175
- contents = patchSrcInContent(contents, asset.url, u.toString());
150
+ contents = MDUtils.replaceSrcInMarkdown(contents, asset.url, u.toString());
176
151
  });
177
152
 
178
153
  if (resource.prepend) {
@@ -256,19 +231,7 @@ export default class PageImporter {
256
231
  }
257
232
 
258
233
  postProcessMD(md) {
259
- let ret = md.replace(/\\\\~/gm, '\\~');
260
-
261
- const match = ret.match(/hlx_replaceTag\(.*?\)/gm);
262
- if (match) {
263
- const hlxReplaceTags = match.filter((i, p, s) => s.indexOf(i) === p);
264
- hlxReplaceTags.forEach((r) => {
265
- const by = r.substring(0, r.length - 1).split('(')[1];
266
- const regex = new RegExp(r.replace('(', '\\(').replace(')', '\\)'), 'gm');
267
- ret = ret.replace(regex, `<${by}>`);
268
- });
269
- }
270
-
271
- return ret;
234
+ return md.replace(/\\\\~/gm, '\\~');
272
235
  }
273
236
 
274
237
  async download(url) {
@@ -20,4 +20,6 @@ export default class PageImporterParams {
20
20
  skipMDFileCreation;
21
21
 
22
22
  logger;
23
+
24
+ docxStylesXML;
23
25
  }
@@ -38,13 +38,11 @@ export default class Blocks {
38
38
  const value = metadata[key];
39
39
  if (value) {
40
40
  if (Array.isArray(value)) {
41
- let list = '';
42
41
  value.forEach((v) => {
43
- // p tags in table are stripped out
44
- // list must receive special hlx_replaceTag command to be post-processed
45
- list += `hlx_replaceTag(p)${v}hlx_replaceTag(/p)`;
42
+ const p = document.createElement('p');
43
+ p.innerHTML = v;
44
+ valueCell.append(p);
46
45
  });
47
- valueCell.textContent = list;
48
46
  } else if (typeof value === 'string') {
49
47
  valueCell.textContent = value;
50
48
  } else {
@@ -79,8 +77,6 @@ export default class Blocks {
79
77
  const cellContent = [];
80
78
  Array.from(cell.childNodes).forEach((c) => cellContent.push(c));
81
79
  rowData.push(cellContent);
82
- } else {
83
- rowData.push(cell);
84
80
  }
85
81
  });
86
82
  data.push(rowData);
@@ -0,0 +1,21 @@
1
+ /*
2
+ * Copyright 2022 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ export default class MDUtils {
14
+ static replaceSrcInMarkdown = (md, oldSrc, newSrc) => {
15
+ if (decodeURI(oldSrc) !== oldSrc) {
16
+ return md.replace(new RegExp(`${decodeURI(oldSrc).replace('.', '\\.')}`, 'gm'), newSrc);
17
+ } else {
18
+ return md.replace(new RegExp(`${oldSrc.replace('.', '\\.').replace('?', '\\?')}`, 'gm'), newSrc);
19
+ }
20
+ };
21
+ }
@@ -202,4 +202,79 @@ describe('PagingExplorer tests', () => {
202
202
  const se = new Test(params);
203
203
  await se.explore();
204
204
  });
205
+
206
+ it('explorer params are honored', async () => {
207
+ const start = 2;
208
+ let callbackCalled = 0;
209
+ let processCalled = 0;
210
+ class Test extends PagingExplorer {
211
+ async fetch() {
212
+ return new Response('test');
213
+ }
214
+
215
+ process() {
216
+ processCalled += 1;
217
+ return [{
218
+ a: processCalled + start - 1,
219
+ }];
220
+ }
221
+ }
222
+
223
+ const se = new Test(params);
224
+ const results = await se.explore(2, () => {
225
+ callbackCalled += 1;
226
+ });
227
+
228
+ strictEqual(callbackCalled, 2, 'callback called twice');
229
+ deepStrictEqual(results, [{ a: 2 }, { a: 3 }], 'result is correct');
230
+ });
231
+
232
+ it('no text in response', async () => {
233
+ let processCalled = 0;
234
+ class Test extends PagingExplorer {
235
+ async fetch() {
236
+ if (processCalled < 2) {
237
+ return new Response('test');
238
+ }
239
+ return new Response('');
240
+ }
241
+
242
+ process() {
243
+ processCalled += 1;
244
+ return [{
245
+ a: processCalled,
246
+ }];
247
+ }
248
+ }
249
+
250
+ const se = new Test(params);
251
+ const results = await se.explore();
252
+
253
+ deepStrictEqual(results, [{ a: 1 }, { a: 2 }], 'result is correct');
254
+ });
255
+
256
+ it('no entries on page', async () => {
257
+ let processCalled = 0;
258
+ class Test extends PagingExplorer {
259
+ async fetch() {
260
+ return new Response('test');
261
+ }
262
+
263
+ process() {
264
+ if (processCalled < 2) {
265
+ processCalled += 1;
266
+ return [{
267
+ a: processCalled,
268
+ }];
269
+ } else {
270
+ return null;
271
+ }
272
+ }
273
+ }
274
+
275
+ const se = new Test(params);
276
+ const results = await se.explore();
277
+
278
+ deepStrictEqual(results, [{ a: 1 }, { a: 2 }], 'result is correct');
279
+ });
205
280
  });
@@ -12,9 +12,12 @@
12
12
 
13
13
  /* eslint-disable max-classes-per-file, class-methods-use-this */
14
14
 
15
+ import path from 'path';
16
+ import fs from 'fs-extra';
15
17
  import { strictEqual, ok } from 'assert';
16
18
  import { describe, it } from 'mocha';
17
19
  import { Response } from 'node-fetch';
20
+ import { dirname } from 'dirname-filename-esm';
18
21
 
19
22
  import { docx2md } from '@adobe/helix-docx2md';
20
23
 
@@ -25,6 +28,9 @@ import MemoryHandler from '../../src/storage/MemoryHandler.js';
25
28
  import MockMediaHandler from '../mocks/MockMediaHandler.js';
26
29
  import NoopLogger from '../mocks/NoopLogger.js';
27
30
 
31
+ // eslint-disable-next-line no-underscore-dangle
32
+ const __dirname = dirname(import.meta);
33
+
28
34
  const logger = new NoopLogger();
29
35
 
30
36
  describe('PageImporter tests', () => {
@@ -83,4 +89,85 @@ describe('PageImporter tests - various options', () => {
83
89
  });
84
90
  strictEqual(md, test, 'valid backward conversion');
85
91
  });
92
+
93
+ it('import - can provide a custom styles.xml', async () => {
94
+ const docxStylesXML = await fs.readFile(path.resolve(__dirname, 'fixtures', 'custom-styles.xml'), 'utf-8');
95
+ const storageHandler = new MemoryHandler(logger);
96
+ const config = {
97
+ storageHandler,
98
+ logger,
99
+ docxStylesXML,
100
+ };
101
+ const se = new Test(config);
102
+ const results = await se.import('/someurl');
103
+
104
+ strictEqual(results.length, 1, 'expect no result');
105
+
106
+ ok(await storageHandler.exists('/someurl/somecomputedpath/resource1.md'), 'md has been stored');
107
+ ok(await storageHandler.exists('/someurl/somecomputedpath/resource1.docx'), 'docx has been stored');
108
+
109
+ const md = await storageHandler.get('/someurl/somecomputedpath/resource1.md');
110
+ strictEqual(md, '# heading1\n\nparagraph\n', 'valid markdown created');
111
+
112
+ const docx = await storageHandler.get('/someurl/somecomputedpath/resource1.docx');
113
+ const test = await docx2md(docx, {
114
+ mediaHandler: new MockMediaHandler(),
115
+ });
116
+ strictEqual(md, test, 'valid backward conversion');
117
+ });
118
+ });
119
+
120
+ describe('PageImporter tests - fixtures', () => {
121
+ const featureTest = async (feature) => {
122
+ class Test extends PageImporter {
123
+ async fetch() {
124
+ const html = await fs.readFile(path.resolve(__dirname, 'fixtures', `${feature}.spec.html`), 'utf-8');
125
+ return new Response(html);
126
+ }
127
+
128
+ async process(document) {
129
+ const pir = new PageImporterResource(feature, '', document.documentElement, null, null);
130
+ return [pir];
131
+ }
132
+ }
133
+
134
+ const storageHandler = new MemoryHandler(logger);
135
+ const config = {
136
+ storageHandler,
137
+ skipDocxConversion: true,
138
+ logger,
139
+ };
140
+ const se = new Test(config);
141
+ const results = await se.import(`https://www.sample.com/${feature}`);
142
+
143
+ strictEqual(results.length, 1, 'expect one result');
144
+
145
+ const md = await storageHandler.get(`/${feature}.md`);
146
+ const expectedMD = await fs.readFile(path.resolve(__dirname, 'fixtures', `${feature}.spec.md`), 'utf-8');
147
+ strictEqual(md.trim(), expectedMD.trim(), 'inported md is expected one');
148
+ };
149
+
150
+ it('import - tables', async () => {
151
+ await featureTest('table');
152
+ });
153
+
154
+ it('import - underlines', async () => {
155
+ await featureTest('u');
156
+ });
157
+
158
+ it('import - links', async () => {
159
+ await featureTest('link');
160
+ });
161
+
162
+ it('import - spans', async () => {
163
+ await featureTest('span');
164
+ });
165
+
166
+ it('import - emphasises', async () => {
167
+ await featureTest('em');
168
+ });
169
+
170
+ it('import - complex', async () => {
171
+ await featureTest('complex');
172
+ });
86
173
  });
@@ -0,0 +1,70 @@
1
+ <div class="js-post-content blogPostMain">
2
+ <section class="blogPostBanner__container">
3
+ <div class="blogPostBanner__imgContainer js-parallax">
4
+ <h1 class="blogPostContent__title">How Sample Performance Management Improved in 2021</h1><img src="https://www.sample.com/blog/wp-content/uploads/How-Sample-Performance-Management-Improved-in-2021_1920x733-scaled.jpg">
5
+ </div>
6
+
7
+ </section>
8
+
9
+ <section class="blogPostContent__wrapper" aria-label="Sample blog post main content" role="main">
10
+
11
+
12
+ <div class="blogPostContent__bg"></div>
13
+ <div class="bhrsection-padding">
14
+ <div class="bhrsection-container-sm">
15
+ <div class="blogPostContent__container blogPostContent__container--product-announcements js-page-color" data-page-color="#1d9336" data-page-color3="#edffc8">
16
+
17
+
18
+
19
+
20
+ <div class="blogPostContentDetail__container">
21
+
22
+ <div class="blogPostContent blogPostContent--default">
23
+ <div class="essb_break_scroll"></div> </div>
24
+
25
+ <div class="blogPostContent">
26
+ <p>Over the course of 2021, we've improved Sample® Performance Management with more flexibility and greater depth, giving you more choice for the when, who, and how of the performance management process at your organization. Here's a quick summary to let you know how to take advantage of these updates.</p>
27
+ <h2>Scheduling Flexibility and Improved Search in Reports</h2>
28
+ <p>These updates give greater flexibility in the cadence and dates you administer assessments and feedback. We separated scheduling for assessments and feedback, so each process can operate on separate timelines depending on your needs. For example, you might choose to have feedback sessions every four months while keeping formal assessments on a six-month schedule.</p>
29
+ <div class="blogPostContent__fullWidthImgContainer">
30
+ <img class="blogPostContent__fullWidthImg lazyload " src="https://www.sample.com/blog/wp-content/uploads/Screen_Shot_2022-01-05_at_9.26.41_AM.png" alt="Screen_Shot_2022-01-05_at_9.26.41_AM">
31
+ </div>
32
+ <p>We also made performance reports searchable by date range instead of tying them to individual Performance Management events.</p>
33
+ <h2>Additional Assessment Flexibility</h2>
34
+ <p>When an employee moves to a new team right before their scheduled assessment, their new manager might not have what they need to make accurate observations. To resolve this issue, you now have the option to <a href="https://help.sample.com/hc/en-us/articles/360060935131-skip-assessments">skip a regularly-scheduled assessment</a>.</p>
35
+ <div class="blogPostContent__fullWidthImgContainer">
36
+ <img class="blogPostContent__fullWidthImg lazyload " src="https://www.sample.com/blog/wp-content/uploads/a66eddc3-4e16-49a9-b597-1151e233790d.png" alt="a66eddc3-4e16-49a9-b597-1151e233790d">
37
+ </div>
38
+ <p>This doesn't mean someone who skips an assessment has to go without feedback for months. Scheduling <a href="https://help.sample.com/hc/en-us/articles/360060531312-impromptu-assessments">impromptu assessments</a> helps you keep employees informed during off-cycle evaluations, such as:</p>
39
+ <ul>
40
+ <li style="font-weight: 400;" aria-level="1">When considering employees for a promotion or raise</li>
41
+ <li style="font-weight: 400;" aria-level="1">When they face disciplinary action</li>
42
+ <li style="font-weight: 400;" aria-level="1">On their anniversary</li>
43
+ <li style="font-weight: 400;" aria-level="1">As part of their onboarding process</li>
44
+ </ul>
45
+ <div class="blogPostContent__fullWidthImgContainer">
46
+ <img class="blogPostContent__fullWidthImg lazyload " src="https://www.sample.com/blog/wp-content/uploads/Impromptu-Assessment-1.png" alt="Impromptu Assessment (1)">
47
+ </div>
48
+ <h2>Expanded Feedback</h2>
49
+ <p>Great feedback can come from anyone who interacts with an employee, from their closest co-workers to employees in a different department who regularly rely on their efforts. We updated our Feedback feature to emphasize this breadth of feedback, expanding the maximum number of reviewers to 10 per employee.</p>
50
+ <p>Feedback is also no longer limited to assessment periods, letting you ask in the moment for what you need. Here are a few examples:</p>
51
+ <ul>
52
+ <li style="font-weight: 400;" aria-level="1">After a big project, the manager wants to see how the team lead performed. They can send feedback requests to everyone involved in it. This could include team members, directors and executives they presented to, or other departments that they trained.</li>
53
+ <li style="font-weight: 400;" aria-level="1">A manager thinks an employee is struggling and wants to know how well the employee works with their peers or with other departments. This feedback can help the manager understand an employee's situation before speaking with them.</li>
54
+ <li style="font-weight: 400;" aria-level="1">An employee is up for a promotion, and the manager would like to understand how the employee works with others.</li>
55
+ </ul>
56
+ <h2>Sample Performance Management: Timely and Targeted</h2>
57
+ <p>These updates help expand Performance Management's reach and help your managers and employees provide meaningful, accurate feedback through continuing conversations. For more information on each of these updates, click the links below:</p>
58
+ <p><a href="https://help.sample.com/hc/en-us/articles/360060533412-Skip-and-Impromptu-Assessments-5-6-2021-">Skip and Impromptu Assessments</a></p>
59
+ <p><a href="https://help.sample.com/hc/en-us/articles/4415512886669-We-ve-Added-Flexibility-to-Performance-Management-12-3-2021-">Impromptu Feedback</a></p>
60
+ <p><a href="https://help.sample.com/hc/en-us/articles/4406463743501-More-Opportunities-for-Peer-Feedback-Participants-8-4-2021-">Feedback Request Limit Increase</a></p>
61
+ <p><a href="https://help.sample.com/hc/en-us/articles/360057083332-Performance-Management-Settings-Update-2-25-2021-">Performance Settings Updates</a></p>
62
+ </div>
63
+
64
+ </div>
65
+
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </section>
70
+ <table><tr><th>Related Posts</th></tr><tr><td><a class="blogPostsBlock__titleLink" href="https://www.sample.com/blog/how-sample-performance-management-improved-in-2021/" aria-label="How Sample Performance Management Improved in 2021">https://www.sample.com/blog/how-sample-performance-management-improved-in-2021/</a></td></tr><tr><td><a class="blogPostsBlock__titleLink" href="https://www.sample.com/blog/announcing-the-product-updates-webpage/" aria-label="Announcing the Product Updates Webpage">https://www.sample.com/blog/announcing-the-product-updates-webpage/</a></td></tr><tr><td><a class="blogPostsBlock__titleLink" href="https://www.sample.com/blog/take-a-look-back-at-2021/" aria-label="Take a Look Back at 2021">https://www.sample.com/blog/take-a-look-back-at-2021/</a></td></tr><tr><td><a class="blogPostsBlock__titleLink" href="https://www.sample.com/blog/whats-new-in-sample-vaccination-tracking-feature/" aria-label="What's New in Sample: Vaccination Tracking Feature">https://www.sample.com/blog/whats-new-in-sample-vaccination-tracking-feature/</a></td></tr></table><table><tr><th colspan="2">Metadata</th></tr><tr><td>Title</td><td>How Sample Performance Management Improved in 2021 - Sample Blog</td></tr><tr><td>Description</td><td>In 2021, we improved Sample® Performance Management with more flexibility and greater depth, giving you finer control of your performance management.</td></tr><tr><td>Category</td><td>Product Announcements</td></tr><tr><td>Author</td><td><a href="https://www.sample.com/blog/author/eserdar/" rel="author" class="fn" style="color: #1d9336;">Eric Serdar</a></td></tr><tr><td>Read Time</td><td> 8 min</td></tr><tr><td>Image</td><td><img src="https://www.sample.com/blog/wp-content/uploads/How-Sample-Performance-Management-Improved-in-2021_1200x628-1024x536.jpg"></td></tr></table></div>
@@ -0,0 +1,54 @@
1
+ # How Sample Performance Management Improved in 2021
2
+
3
+ ![](https://www.sample.com/blog/wp-content/uploads/How-Sample-Performance-Management-Improved-in-2021_1920x733-scaled.jpg)
4
+
5
+ Over the course of 2021, we've improved Sample® Performance Management with more flexibility and greater depth, giving you more choice for the when, who, and how of the performance management process at your organization. Here's a quick summary to let you know how to take advantage of these updates.
6
+
7
+ ## Scheduling Flexibility and Improved Search in Reports
8
+
9
+ These updates give greater flexibility in the cadence and dates you administer assessments and feedback. We separated scheduling for assessments and feedback, so each process can operate on separate timelines depending on your needs. For example, you might choose to have feedback sessions every four months while keeping formal assessments on a six-month schedule.
10
+
11
+ ![Screen\_Shot\_2022-01-05\_at\_9.26.41\_AM](https://www.sample.com/blog/wp-content/uploads/Screen_Shot_2022-01-05_at_9.26.41_AM.png)
12
+
13
+ We also made performance reports searchable by date range instead of tying them to individual Performance Management events.
14
+
15
+ ## Additional Assessment Flexibility
16
+
17
+ When an employee moves to a new team right before their scheduled assessment, their new manager might not have what they need to make accurate observations. To resolve this issue, you now have the option to [skip a regularly-scheduled assessment](https://help.sample.com/hc/en-us/articles/360060935131-skip-assessments).
18
+
19
+ ![a66eddc3-4e16-49a9-b597-1151e233790d](https://www.sample.com/blog/wp-content/uploads/a66eddc3-4e16-49a9-b597-1151e233790d.png)
20
+
21
+ This doesn't mean someone who skips an assessment has to go without feedback for months. Scheduling [impromptu assessments](https://help.sample.com/hc/en-us/articles/360060531312-impromptu-assessments) helps you keep employees informed during off-cycle evaluations, such as:
22
+
23
+ - When considering employees for a promotion or raise
24
+ - When they face disciplinary action
25
+ - On their anniversary
26
+ - As part of their onboarding process
27
+
28
+ ![Impromptu Assessment (1)](https://www.sample.com/blog/wp-content/uploads/Impromptu-Assessment-1.png)
29
+
30
+ ## Expanded Feedback
31
+
32
+ Great feedback can come from anyone who interacts with an employee, from their closest co-workers to employees in a different department who regularly rely on their efforts. We updated our Feedback feature to emphasize this breadth of feedback, expanding the maximum number of reviewers to 10 per employee.
33
+
34
+ Feedback is also no longer limited to assessment periods, letting you ask in the moment for what you need. Here are a few examples:
35
+
36
+ - After a big project, the manager wants to see how the team lead performed. They can send feedback requests to everyone involved in it. This could include team members, directors and executives they presented to, or other departments that they trained.
37
+ - A manager thinks an employee is struggling and wants to know how well the employee works with their peers or with other departments. This feedback can help the manager understand an employee's situation before speaking with them.
38
+ - An employee is up for a promotion, and the manager would like to understand how the employee works with others.
39
+
40
+ ## Sample Performance Management: Timely and Targeted
41
+
42
+ These updates help expand Performance Management's reach and help your managers and employees provide meaningful, accurate feedback through continuing conversations. For more information on each of these updates, click the links below:
43
+
44
+ [Skip and Impromptu Assessments](https://help.sample.com/hc/en-us/articles/360060533412-Skip-and-Impromptu-Assessments-5-6-2021-)
45
+
46
+ [Impromptu Feedback](https://help.sample.com/hc/en-us/articles/4415512886669-We-ve-Added-Flexibility-to-Performance-Management-12-3-2021-)
47
+
48
+ [Feedback Request Limit Increase](https://help.sample.com/hc/en-us/articles/4406463743501-More-Opportunities-for-Peer-Feedback-Participants-8-4-2021-)
49
+
50
+ [Performance Settings Updates](https://help.sample.com/hc/en-us/articles/360057083332-Performance-Management-Settings-Update-2-25-2021-)
51
+
52
+ <table><tbody><tr><th>Related Posts</th></tr><tr><td><a class="blogPostsBlock__titleLink" href="https://www.sample.com/blog/how-sample-performance-management-improved-in-2021/" aria-label="How Sample Performance Management Improved in 2021">https://www.sample.com/blog/how-sample-performance-management-improved-in-2021/</a></td></tr><tr><td><a class="blogPostsBlock__titleLink" href="https://www.sample.com/blog/announcing-the-product-updates-webpage/" aria-label="Announcing the Product Updates Webpage">https://www.sample.com/blog/announcing-the-product-updates-webpage/</a></td></tr><tr><td><a class="blogPostsBlock__titleLink" href="https://www.sample.com/blog/take-a-look-back-at-2021/" aria-label="Take a Look Back at 2021">https://www.sample.com/blog/take-a-look-back-at-2021/</a></td></tr><tr><td><a class="blogPostsBlock__titleLink" href="https://www.sample.com/blog/whats-new-in-sample-vaccination-tracking-feature/" aria-label="What&#x27;s New in Sample: Vaccination Tracking Feature">https://www.sample.com/blog/whats-new-in-sample-vaccination-tracking-feature/</a></td></tr></tbody></table>
53
+
54
+ <table><tbody><tr><th colspan="2">Metadata</th></tr><tr><td>Title</td><td>How Sample Performance Management Improved in 2021 - Sample Blog</td></tr><tr><td>Description</td><td>In 2021, we improved Sample® Performance Management with more flexibility and greater depth, giving you finer control of your performance management.</td></tr><tr><td>Category</td><td>Product Announcements</td></tr><tr><td>Author</td><td><a href="https://www.sample.com/blog/author/eserdar/" rel="author" class="fn" style="color: #1d9336;">Eric Serdar</a></td></tr><tr><td>Read Time</td><td>8 min</td></tr><tr><td>Image</td><td><img src="https://www.sample.com/blog/wp-content/uploads/How-Sample-Performance-Management-Improved-in-2021_1200x628-1024x536.jpg"></td></tr></tbody></table>